Twórcy YouTube muszą oceniać wydajność swoich filmów; analizowanie pozytywnych i negatywnych komentarzy oraz porównywanie swoich treści z innymi w tej samej lub innej kategorii staje się niezbędne.
Ręczne przeglądanie opublikowanych filmów może być żmudne i czasochłonne dla twórców. Właśnie w tym przypadku skrypt skrobiący YouTube staje się nieoceniony. W tym przewodniku opracujemy skrypt YouTube przeznaczony do automatyzacji procesu gromadzenia danych.
Aby skrypt działał poprawnie, musimy zainstalować kilka pakietów. Pierwszym pakietem do zainstalowania jest selenium-wire, rozszerzenie Selenium, które umożliwia prawidłową konfigurację proxy, oraz samo Selenium dla niezbędnych klas i modułów. Aby zainstalować te pakiety, wykonaj następujące polecenie w interfejsie poleceń:
pip install selenium-wire selenium blinker==1.7.0
Now let's focus on the imports.
Na tym etapie ważne jest, aby zaimportować biblioteki i pakiety, które będą wykorzystywane w naszym skrypcie do interakcji z elementami sieci. Dodatkowo powinniśmy dołączyć moduły do przetwarzania danych i zarządzania czasem wykonywania, aby zapewnić wydajne wykonywanie skryptu.
from selenium.webdriver.chrome.options import Options
from seleniumwire import webdriver as wiredriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
import json
import time
Moduł json pomaga w konwersji wyodrębnionych danych na odpowiednio sformatowane dane JSON, zapewniając optymalną prezentację danych. Pomimo maskowania naszego IP, moduł time jest niezbędny do wprowadzenia losowości do działań, zapobiegając pojawianiu się zachowań podobnych do skryptów.
Dodatkowo, moduł ten jest kluczowy dla zapewnienia, że elementy, których potrzebujemy do wyodrębnienia danych ze strony, zostały załadowane. Pozostałe importy składają się z niezbędnych klas lub podmodułów, które wykonują różne działania i zostaną omówione w kolejnych sekcjach kodu.
Za każdym razem, gdy uruchamiasz instancję Selenium za pomocą skryptu w Pythonie, skrypt używa naszego adresu IP do dowolnej czynności, którą chcemy wykonać. Jest to niebezpieczne, szczególnie w przypadku witryn takich jak YouTube, które mają ścisłe zasady dotyczące zakazu pobierania informacji z ich witryny. Konsekwencją tego mogą być tymczasowe ograniczenia w dostępie do treści YouTube.
Aby tego uniknąć, musimy zrobić kilka rzeczy. Musimy utworzyć 3 zmienne, aby przechowywać szczegóły serwera proxy, przez który będziemy uzyskiwać dostęp do strony. Następnie tworzymy zmienną opcji, chrome_options, którą przekażemy do instancji Chrome WebDriver, aby Selenium wiedziało, którego proxy użyć podczas skrobania. Przekazujemy szczegóły proxy jako argumenty dla chrome_options i nasze proxy jest ustawione.
# Określ adres serwera proxy wraz z nazwą użytkownika i hasłem.
proxy_address = ""
proxy_username = ""
proxy_password = ""
# Skonfiguruj opcje Chrome z proxy i uwierzytelnianiem
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')
# Utwórz instancję WebDriver za pomocą selenium-wire
driver = wiredriver.Chrome(options=chrome_options)
Utwórz zmienną o nazwie "youtube_url_to_scrape", aby przechowywać adres URL strony docelowej YouTube. Zmienna ta jest następnie wykorzystywana w metodzie "driver.get()", aby skierować Selenium do otwarcia określonej strony w celu skrobania. Wykonanie tej akcji spowoduje otwarcie osobnego okna Chrome po uruchomieniu skryptu.
youtube_url_to_scrape = ""
# Wykonaj automatyzację Selenium z rozszerzonymi możliwościami selenium-wire
driver.get(youtube_url_to_scrape)
Następnie definiujemy funkcję "extract _information()", która, jak sama nazwa wskazuje, wyodrębnia niezbędne informacje ze strony.
Ważne jest, aby upewnić się, że wszystkie elementy na stronie zostały załadowane. Aby to zrobić, używamy klasy WebDriverWait do wstrzymania skryptu przynajmniej do momentu, gdy przycisk "więcej" będzie dostępny i kliknięty, co jest zaimplementowane w zmiennej "element". Gdy przycisk jest dostępny, Selenium wykonuje akcję kliknięcia JavaScript, która umożliwia dostęp do pełnego opisu filmu.
Aby rozwiązać wspomniany wcześniej problem dynamicznych komentarzy, wdrażamy rozwiązanie eliminujące wszelkie powiązane kwestie. Korzystając z klasy Actions i modułu Time, przewijamy w dół dwukrotnie co 10 sekund, upewniając się, że zeskrobaliśmy jak najwięcej komentarzy. To proaktywne podejście chroni przed potencjalnymi wąskimi gardłami związanymi z dynamicznie ładowaną zawartością.
def extract_information() -> dict:
try:
element = WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.XPATH, '//*[@id="expand"]'))
)
element.click()
time.sleep(10)
actions = ActionChains(driver)
actions.send_keys(Keys.END).perform()
time.sleep(10)
actions.send_keys(Keys.END).perform()
time.sleep(10)
Istnieją różne sposoby wyszukiwania elementów za pomocą selenium webdriver. Można wyszukiwać według ID, CLASS_NAME, XPATH itp. W tym przewodniku będziemy używać kombinacji, a nie tylko jednej metody.
XPATH to bardziej skomplikowany, ale oparty na wzorcach system lokalizowania zmiennych podczas skrobania. Jest uważany za najbardziej skomplikowany; jednak Chrome sprawił, że jest to łatwe.
Podczas przeglądania kodu za pomocą narzędzia inspekcji Chrome, wystarczy kliknąć prawym przyciskiem myszy, aby skopiować XPATH. Po skopiowaniu można użyć funkcji `find_elements`, aby zidentyfikować wszystkie elementy zawierające żądane informacje, takie jak tytuł wideo, opis itp.
Należy pamiętać, że niektóre elementy na stronie mogą mieć podobne atrybuty, co może spowodować, że funkcja `find_elements()` zwróci listę zamiast ciągu znaków. W takich przypadkach należy przeanalizować listę, aby wskazać indeks odpowiednich informacji i wyodrębnić tekst.
Na koniec zwracana jest zmienna słownikowa o nazwie `data`, zawierająca wszystkie informacje zebrane podczas skrobania, ergo, niezbędna dla następnej sekcji.
video_title = driver.find_elements(By.XPATH, '//*[@id="title"]/h1')[0].text
owner = driver.find_elements(By.XPATH, '//*[@id="text"]/a')[0].text
total_number_of_subscribers = \
driver.find_elements(By.XPATH, "//div[@id='upload-info']//yt-formatted-string[@id='owner-sub-count']")[
0].text
video_description = driver.find_elements(By.XPATH, '//*[@id="description-inline-expander"]/yt-attributed-string/span/span')
result = []
for i in video_description:
result.append(i.text)
description = ''.join(result)
publish_date = driver.find_elements(By.XPATH, '//*[@id="info"]/span')[2].text
total_views = driver.find_elements(By.XPATH, '//*[@id="info"]/span')[0].text
number_of_likes = driver.find_elements(By.XPATH, '//*[@id="top-level-buttons-computed"]/segmented-like-dislike-button-view-model/yt-smartimation/div/div/like-button-view-model/toggle-button-view-model/button-view-model/button/div')[
1].text
comment_names = driver.find_elements(By.XPATH, '//*[@id="author-text"]/span')
comment_content = driver.find_elements(By.XPATH, '//*[@id="content-text"]/span')
comment_library = []
for each in range(len(comment_names)):
name = comment_names[each].text
content = comment_content[each].text
indie_comment = {
'name': name,
'comment': content
}
comment_library.append(indie_comment)
data = {
'owner': owner,
'subscribers': total_number_of_subscribers,
'video_title': video_title,
'description': description,
'date': publish_date,
'views': total_views,
'likes': number_of_likes,
'comments': comment_library
}
return data
except Exception as err:
print(f"Error: {err}")
def organize_write_data(data:dict):
output = json.dumps(data, indent=2, ensure_ascii=False).encode("ascii", "ignore").decode("utf-8")
try:
with open("output.json", 'w', encoding='utf-8') as file:
file.write(output)
except Exception as err:
print(f"Error encountered: {err}")
Funkcja `organize_write_data()` pobiera zwrócone `data` jako dane wejściowe i organizuje je w sformatowaną strukturę JSON. Następnie zapisuje te zorganizowane dane do pliku wyjściowego o nazwie "output.json", jednocześnie obsługując potencjalne błędy podczas procesu zapisu pliku.
Póki co, oto pełny kod naszego programu scrapingowego:
from selenium.webdriver.chrome.options import Options
from seleniumwire import webdriver as wiredriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
import json
import time
# Określ adres serwera proxy wraz z nazwą użytkownika i hasłem.
proxy_address = ""
proxy_username = ""
proxy_password = ""
# Skonfiguruj opcje Chrome z proxy i uwierzytelnianiem
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')
# Utwórz instancję WebDriver za pomocą selenium-wire
driver = wiredriver.Chrome(options=chrome_options)
youtube_url_to_scrape = ""
# Wykonaj automatyzację Selenium z rozszerzonymi możliwościami selenium-wire
driver.get(youtube_url_to_scrape)
def extract_information() -> dict:
try:
element = WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.XPATH, '//*[@id="expand"]'))
)
element.click()
time.sleep(10)
actions = ActionChains(driver)
actions.send_keys(Keys.END).perform()
time.sleep(10)
actions.send_keys(Keys.END).perform()
time.sleep(10)
video_title = driver.find_elements(By.XPATH, '//*[@id="title"]/h1')[0].text
owner = driver.find_elements(By.XPATH, '//*[@id="text"]/a')[0].text
total_number_of_subscribers = \
driver.find_elements(By.XPATH, "//div[@id='upload-info']//yt-formatted-string[@id='owner-sub-count']")[
0].text
video_description = driver.find_elements(By.XPATH,
'//*[@id="description-inline-expander"]/yt-attributed-string/span/span')
result = []
for i in video_description:
result.append(i.text)
description = ''.join(result)
publish_date = driver.find_elements(By.XPATH, '//*[@id="info"]/span')[2].text
total_views = driver.find_elements(By.XPATH, '//*[@id="info"]/span')[0].text
number_of_likes = driver.find_elements(By.XPATH,
'//*[@id="top-level-buttons-computed"]/segmented-like-dislike-button-view-model/yt-smartimation/div/div/like-button-view-model/toggle-button-view-model/button-view-model/button/div')[
1].text
comment_names = driver.find_elements(By.XPATH, '//*[@id="author-text"]/span')
comment_content = driver.find_elements(By.XPATH,
'//*[@id="content-text"]/span')
comment_library = []
for each in range(len(comment_names)):
name = comment_names[each].text
content = comment_content[each].text
indie_comment = {
'name': name,
'comment': content
}
comment_library.append(indie_comment)
data = {
'owner': owner,
'subscribers': total_number_of_subscribers,
'video_title': video_title,
'description': description,
'date': publish_date,
'views': total_views,
'likes': number_of_likes,
'comments': comment_library
}
return data
except Exception as err:
print(f"Error: {err}")
# Zapis danych w formacie JSON
def organize_write_data(data: dict):
output = json.dumps(data, indent=2, ensure_ascii=False).encode("ascii", "ignore").decode("utf-8")
try:
with open("output.json", 'w', encoding='utf-8') as file:
file.write(output)
except Exception as err:
print(f"Error encountered: {err}")
organize_write_data(extract_information())
driver.quit()
Wynik wygląda następująco:
Bezpieczne wykorzystanie bogactwa informacji YouTube jest znacznie korzystne, gdy stosowane są dobrze opracowane skrypty, które wykorzystują serwery proxy w celu zapewnienia zgodności z zasadami i przepisami platformy. Omówione powyżej podejście ułatwia odpowiedzialną ekstrakcję danych i zmniejsza ryzyko potencjalnych ograniczeń nałożonych przez platformę.
Komentarze: 0