Як скрапити дані з YouTube за допомогою Python

Коментарі: 0

Творцям контенту на YouTube необхідно ретельно вивчати метрики відео, аналізувати коментарі, а також проводити порівняння своїх робіт з відео інших авторів. Незамінним інструментом для виконання аналізу необхідної вибірки відео є скрипт для скрапінгу YouTube. У цьому посібнику ми розробимо скрипт, який дасть змогу автоматизувати процес збору таких даних, як власник, назва, опис каналу та коментарі під відео.

Створення скрапера для вилучення даних з YouTube

Для правильної роботи скрипта необхідно встановити кілька пакетів. Почнемо з встановлення "selenium-wire", який забезпечує розширені можливості для налаштування проксі, а також сам Selenium, що містить основні класи та модулі. Для встановлення цих пакетів введіть таку команду в командному рядку:

pip install selenium-wire selenium blinker==1.7.0

Тепер перейдемо до імпорту додаткових компонентів.

Крок 1: Імпорт бібліотек і пакетів

На даному етапі необхідно імпортувати бібліотеки та пакети, які будуть використані в нашому скрипті для роботи з вебелементами, а також додаткові модулі для обробки даних і керування часом виконання.

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

Модуль "json" відіграє ключову роль у перетворенні витягнутих даних у правильно відформатовані JSON-структури, що забезпечує їх оптимальне відображення. Крім використання маскування IP-адреси, модуль "time" також необхідний для впровадження випадкових затримок між діями скрипта, що допомагає мінімізувати ризик виявлення скриптів за поведінковими патернами.

Крім того, модуль time важливий для забезпечення коректного завантаження елементів на сторінці, з яких належить витягувати дані. У наступних розділах ми більш детально розглянемо інші використовувані модулі.

Крок 2: Налаштування драйвера Chrome для Selenium

Щоразу, коли ви запускаєте Selenium за допомогою Python-скрипта, він використовує вашу IP-адресу для виконання будь-яких дій. Це може бути ризиковано, особливо для сайтів із системами захисту від скрапінгу, таких як YouTube. Можливими наслідками можуть бути обмеження на доступ до контенту YouTube з вашої IP-адреси.

Для уникнення таких ситуацій необхідно вжити кілька дій. Спочатку створіть три змінні для зберігання даних про проксі, через які буде здійснюватися доступ до сторінки. Потім створіть змінну "chrome_options", яку інтегруємо в Chrome WebDriver. Це дасть змогу Selenium визначити, який проксі використовувати під час скрапінгу. Дані проксі необхідно направити у вигляді аргументів для "chrome_options", таким чином завершуючи інтеграцію їх у скрипт.

# Вкажіть адресу проксі-сервера з логіном і паролем
proxy_address = ""
proxy_username = ""
proxy_password = ""
# Налаштуйте параметри Chrome для роботи з проксі
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')
# Створіть екземпляр WebDriver із selenium-wire
driver = wiredriver.Chrome(options=chrome_options)

Крок 3: Витяг даних зі сторінки відео на YouTube

Створіть змінну "youtube_url_to_scrape", яка зберігатиме URL цільової сторінки на YouTube. Ця змінна потім використовується в методі "driver.get()", щоб вказати Selenium відкрити певну сторінку для скрапінгу. Ця дія призведе до відкриття окремого вікна Chrome під час виконання скрипта.

youtube_url_to_scrape = ""
# Виконайте автоматизацію Selenium, використовуючи selenium-wire.
driver.get(youtube_url_to_scrape)

Тепер визначимо функцію "extract_information()", яка витягує необхідну інформацію зі сторінки.

Важливо переконатися, що всі елементи на сторінці завантажені. Для цього завдання ми використовуємо клас WebDriverWait для призупинення виконання скрипта до моменту, коли кнопка "more" стане доступна для взаємодії, що досягається за допомогою змінної element. Коли кнопка стає доступною, Selenium виконує JavaScript-команду для її натискання, дозволяючи тим самим отримати доступ до повного опису відео.

Для вирішення проблеми динамічно завантажуваних коментарів ми застосовуємо клас "Actions" у поєднанні з модулем "time". Це дозволяє нам прокручувати сторінку вниз двічі кожні 10 секунд, забезпечуючи ефективніший збір коментарів. Такий підхід гарантує, що ми зможемо скрапити максимальну кількість коментарів.

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)

Існує кілька способів пошуку елементів із використанням Selenium WebDriver: за ID, CLASS_NAME, XPATH тощо. У цьому посібнику ми будемо використовувати їхню комбінацію.

XPATH - це складніша, заснована на шаблонах система для пошуку змінних під час скрапінгу. Chrome дасть змогу спростити процес її використання: під час перегляду коду за допомогою інструменту інспекції Chrome клацніть правою кнопкою миші, щоб скопіювати XPATH. Після копіювання ви можете використовувати функцію "find_elements", щоб визначити всі елементи, що містять потрібну інформацію, наприклад, заголовок відео, опис тощо.

Важливо зазначити, що деякі елементи на сторінці можуть мати схожі атрибути, що може призвести до того, що виклик "find_elements()" поверне список, а не рядок. У таких випадках необхідно вивчити список, щоб визначити індекс релевантної інформації та витягти текст.

На завершення, створюється змінна-словник "data", яка повертатиме всю зібрану інформацію для подальшого використання.

 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}")

Крок 4: Запис зібраних даних у 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()", яка приймає дані у вигляді словника data і організовує їх у структурований формат JSON. Після цього функція записує ці дані до файлу з назвою "output.json", водночас враховуючи й обробляючи можливі помилки, які можуть виникнути в процесі запису файлу.

Повний код

Нижче представлено повну версію скрипта для вилучення даних:

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

# Вкажіть адресу проксі-сервера з логіном і паролем
proxy_address = ""
proxy_username = ""
proxy_password = ""

# Налаштуйте параметри Chrome для роботи з проксі
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')

# Створіть екземпляр WebDriver із selenium-wire
driver = wiredriver.Chrome(options=chrome_options)

youtube_url_to_scrape = ""

# Виконуйте автоматизацію в Selenium з розширеними можливостями 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}")


# Запис даних у 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()

Результати

Вихідні дані мають такий вигляд:

Screenshot_1.png

Безпечний збір інформації з YouTube стає особливо ефективним, коли використовуються добре розроблені скрипти із застосуванням проксі для гарантії дотримання політик і правил платформи. Розглянутий вище підхід сприяє відповідальному вилученню даних і допомагає знизити ризик можливих обмежень, що накладаються платформою.

Коментарії:

0 Коментаріїв