Como recolher dados vitais do YouTube com Python

Comentários: 0

Os criadores do YouTube têm de avaliar o desempenho dos seus vídeos; a análise dos comentários positivos e negativos e a comparação dos seus conteúdos com outros da mesma categoria ou de categorias diferentes tornam-se essenciais.

A análise manual dos vídeos publicados pode ser entediante e demorada para os criadores. É precisamente aqui que um script de raspagem do YouTube se torna inestimável. Neste guia, desenvolveremos um script do YouTube projetado para automatizar o processo de coleta de dados.

Criando um scraper para extrair dados do YouTube

Para que o script funcione corretamente, precisamos instalar alguns pacotes. O primeiro pacote a ser instalado é o selenium-wire, uma extensão do Selenium que permite a configuração adequada do proxy, e o próprio Selenium para classes e módulos essenciais. Para instalar esses pacotes, execute o seguinte comando na sua interface de comando:

pip install selenium-wire selenium blinker==1.7.0

Agora vamos concentrar-nos nas importações.

Passo 1: Importando bibliotecas e pacotes

Nesta etapa, é importante importar as bibliotecas e pacotes que serão utilizados em nosso script para interagir com elementos da Web. Além disso, devemos incluir módulos para processamento de dados e gerenciamento de tempo de execução para garantir a execução eficiente do script.

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

O módulo json ajuda a converter os dados extraídos em dados JSON devidamente formatados, garantindo uma apresentação óptima dos dados. Apesar de mascarar o nosso IP, o módulo time é essencial para introduzir aleatoriedade nas acções, impedindo o aparecimento de comportamentos semelhantes a scripts.

Além disso, este módulo é crucial para garantir que os elementos que precisamos para extrair dados da página tenham sido carregados. As importações restantes consistem em classes ou submódulos necessários que executam ações distintas e serão elaboradas nas seções subsequentes do código.

Passo 2: configurando o driver do Selenium para Chrome

Sempre que executamos uma instância do selenium usando um script em python, o script usa nosso endereço IP para qualquer atividade que desejamos realizar. Isso é perigoso, especialmente para sites como o YouTube, com políticas rígidas contra a extração de informações de seu site, você pode verificar o arquivo de robôs para uma melhor referência. As consequências disto podem ser restrições temporárias no seu IP para aceder ao conteúdo do YouTube.

Para evitar tudo isso, há algumas coisas que precisamos fazer. Precisamos criar 3 variáveis para abrigar os detalhes do proxy através do qual estaríamos acessando a página. Em seguida, criamos uma variável de opções, chrome_options, que passaremos para a instância do Chrome WebDriver para que o Selenium saiba qual proxy usar ao fazer o scraping. Passamos os detalhes do proxy como argumentos para chrome_options e nosso proxy está definido.

# Especificar o endereço do servidor proxy com nome de utilizador e palavra-passe
proxy_address = ""
proxy_username = ""
proxy_password = ""
# Configurar as opções do Chrome com o proxy e a autenticação
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')
# Criar uma instância WebDriver com selenium-wire
driver = wiredriver.Chrome(options=chrome_options)

Passo 3: Extrair as informações da página de vídeo do YouTube

Crie uma variável chamada "youtube_url_to_scrape" para armazenar o URL da página de destino do YouTube. Essa variável é então utilizada no método "driver.get()" para direcionar o Selenium para abrir uma página específica para raspagem. A execução dessa ação abrirá uma janela separada do Chrome quando o script for executado.

youtube_url_to_scrape = ""
# Execute sua automação Selenium com os recursos aprimorados do selenium-wire
driver.get(youtube_url_to_scrape)

Em seguida, definimos a função "extract _information()", que, como o nome sugere, extrai as informações necessárias da página.

É importante garantir que todos os elementos da página sejam carregados. Para fazer isso, usamos a classe WebDriverWait para pausar o script pelo menos até que o botão "mais" esteja disponível e seja clicado, o que é implementado na variável "elemento". Uma vez disponível, o Selenium executa uma ação de clique em JavaScript que permite o acesso à descrição completa do vídeo.

Para resolver o problema do comentário dinâmico mencionado anteriormente, estamos implementando uma solução para eliminar quaisquer problemas relacionados. Usando a classe Actions e o módulo Time, rolamos para baixo duas vezes a cada 10 segundos, certificando-nos de raspar o maior número possível de comentários. Essa abordagem proativa protege contra possíveis gargalos associados ao conteúdo carregado dinamicamente.

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)

Existem diferentes maneiras de pesquisar elementos usando o selenium webdriver. Você pode pesquisar por ID, CLASS_NAME, XPATH, etc. Para este guia, usaremos uma combinação em vez de apenas um método.

O XPATH é um sistema mais complexo, porém baseado em padrões, para localizar variáveis durante a raspagem. Ele é considerado o mais complicado; no entanto, o Chrome facilitou isso.

Ao revisar o código usando a ferramenta de inspeção do Chrome, basta clicar com o botão direito do mouse para copiar o XPATH. Uma vez copiado, você pode usar a função `find_elements` para identificar todos os elementos que contêm as informações desejadas, como o título do vídeo, a descrição, etc.

É crucial notar que certos elementos na página podem compartilhar atributos similares, o que pode fazer com que a chamada `find_elements()` retorne uma lista ao invés de uma string. Nesses casos, é necessário examinar a lista para identificar o índice das informações relevantes e extrair o texto.

Concluindo, uma variável de dicionário chamada `data` é retornada, encapsulando todas as informações coletadas durante a raspagem, portanto, essencial para a seção subsequente.

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

Passo 4: Escrever os dados recolhidos num ficheiro 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}")

A função `organize_write_data()` recebe os `dados` retornados como entrada e os organiza em uma estrutura JSON formatada. Em seguida, ele grava esses dados organizados em um arquivo de saída chamado "output.json" enquanto trata possíveis erros durante o processo de gravação de arquivos.

Código completo

Até agora, aqui está o código completo do nosso programa de raspagem:

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

# Especificar o endereço do servidor proxy com nome de utilizador e palavra-passe
proxy_address = ""
proxy_username = ""
proxy_password = ""

# Configurar as opções do Chrome com o proxy e a autenticação
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')

# Criar uma instância WebDriver com selenium-wire
driver = wiredriver.Chrome(options=chrome_options)

youtube_url_to_scrape = ""

# Realize a sua automatização Selenium com as capacidades melhoradas do 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}")


# Registar dados em 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()

Resultados

O resultado tem o seguinte aspeto:

Screenshot_1.png

O aproveitamento seguro da riqueza de informações do YouTube é significativamente benéfico quando são utilizados scripts bem elaborados que utilizam proxies para garantir a adesão às políticas e regulamentos da plataforma. A abordagem acima descrita facilita a extração responsável de dados e reduz o risco de potenciais restrições impostas pela plataforma.

Comentários:

0 Comentários