Cómo extraer datos vitales de YouTube con Python

Comentarios: 0

Los creadores de YouTube tienen que evaluar el rendimiento de sus vídeos; analizar los comentarios positivos y negativos, y comparar su contenido con otros de la misma categoría o de categorías diferentes se convierte en algo esencial.

Examinar manualmente los vídeos publicados puede resultar tedioso y llevar mucho tiempo a los creadores. Aquí es precisamente donde un script de scraping de YouTube se vuelve invaluable. En esta guía desarrollaremos un script de YouTube diseñado para automatizar el proceso de recopilación de datos.

Creación de un scraper para extraer datos de YouTube

Para que el script funcione correctamente, necesitamos instalar algunos paquetes. El primer paquete a instalar es selenium-wire, una extensión de Selenium que permite configurar correctamente el proxy, y el propio Selenium para las clases y módulos esenciales. Para instalar estos paquetes, ejecuta el siguiente comando en tu interfaz de comandos:

pip install selenium-wire selenium blinker==1.7.0

Ahora centrémonos en las importaciones.

Paso 1: Importar librerías y paquetes

En esta fase, es importante importar las librerías y paquetes que se utilizarán en nuestro script para interactuar con los elementos web. Además, debemos incluir módulos para el procesamiento de datos y la gestión en tiempo de ejecución para asegurar una ejecución eficiente del 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

El módulo json ayuda a convertir los datos extraídos en datos JSON formateados correctamente, asegurando una presentación óptima de los datos. A pesar de enmascarar nuestra IP, el módulo time es esencial para introducir aleatoriedad a las acciones, evitando la aparición de comportamientos tipo script.

Además, este módulo es crucial para asegurar que los elementos que necesitamos para extraer datos de la página se han cargado. El resto de importaciones consisten en clases o submódulos necesarios que realizan distintas acciones y sobre los que se profundizará en las siguientes secciones del código.

Paso 2: Configuración del controlador de Selenium para Chrome

Siempre que se ejecuta una instancia de selenium mediante un script en python, el script utiliza nuestra dirección IP para cualquier actividad que queramos realizar. Esto es peligroso, especialmente para sitios web como YouTube con políticas estrictas contra el scraping de información de su sitio web, puedes revisar su archivo robots para una mejor referencia. Las consecuencias de esto podrían ser restricciones temporales en tu IP para acceder al contenido de YouTube.

Para evitar todo esto, tenemos que hacer un par de cosas. Necesitamos crear 3 variables para albergar los detalles del proxy a través del cual estaríamos accediendo a la página. A continuación creamos una variable de opciones, chrome_options, que pasaremos a la instancia de Chrome WebDriver para que Selenium sepa qué proxy utilizar al hacer el scraping. Pasamos los detalles del proxy como argumentos de chrome_options y ya tenemos configurado nuestro proxy.

# Especifique la dirección del servidor proxy con el nombre de usuario y la contraseña
proxy_address = ""
proxy_username = ""
proxy_password = ""
# Configurar las opciones de Chrome con el proxy y la autenticación
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')
# Crear una instancia WebDriver con selenium-wire
driver = wiredriver.Chrome(options=chrome_options)

Paso 3: Extraer la información de la página de vídeos de YouTube

Crear una variable llamada "youtube_url_to_scrape" para almacenar la URL de la página de destino de YouTube. Esta variable luego se utiliza en el método "driver.get()" para dirigir Selenium para abrir una página específica para el raspado. Al ejecutar esta acción se abrirá una ventana independiente de Chrome cuando se ejecute el script.

youtube_url_to_scrape = ""
# Realice su automatización Selenium con las capacidades mejoradas de selenium-wire
driver.get(youtube_url_to_scrape)

A continuación, definimos la función "extract _information()", que, como su nombre indica, extrae la información necesaria de la página.

Es importante asegurarse de que se cargan todos los elementos de la página. Para ello, utilizamos la clase WebDriverWait para pausar el script al menos hasta que esté disponible y se pulse el botón "more", que se implementa bajo la variable "element". Una vez disponible, Selenium ejecuta una acción de clic en JavaScript que permite acceder a la descripción completa del vídeo.

Para abordar el problema de los comentarios dinámicos mencionado anteriormente, estamos implementando una solución para eliminar cualquier problema relacionado. Utilizando la clase Actions y el módulo Time, nos desplazamos hacia abajo dos veces cada 10 segundos, asegurándonos de raspar tantos comentarios como sea posible. Este enfoque proactivo protege contra los posibles cuellos de botella asociados con el contenido cargado dinámicamente.

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)

Hay diferentes formas de buscar elementos usando selenium webdriver. Puede buscar por ID, CLASS_NAME, XPATH, etc. Para esta guía, vamos a utilizar una combinación en lugar de un solo método.

XPATH es un sistema más intrincado pero basado en patrones para localizar variables durante el scraping. Se considera el más complicado; sin embargo, Chrome lo ha facilitado.

Mientras revisa el código utilizando la herramienta de inspección de Chrome, simplemente haga clic con el botón derecho para copiar el XPATH. Una vez copiado, puede utilizar la función `find_elements` para identificar todos los elementos que contienen la información deseada, como el título del vídeo, la descripción, etc.

Es crucial tener en cuenta que algunos elementos de la página pueden compartir atributos similares, lo que puede hacer que la llamada `find_elements()` devuelva una lista en lugar de una cadena. En estos casos, deberás examinar la lista para localizar el índice de la información relevante y extraer el texto.

Por último, se devuelve una variable de diccionario llamada `data`, que encapsula toda la información recopilada durante el scraping, ergo, esencial para la sección posterior.

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

Paso 4: Escribir los datos recopilados en un archivo 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}")

La función `organize_write_data()` toma los `data` devueltos como entrada y los organiza en una estructura JSON formateada. A continuación, escribe estos datos organizados en un archivo de salida llamado "output.json" mientras maneja los posibles errores durante el proceso de escritura del archivo.

Código completo

Hasta aquí el código completo de nuestro programa de scraping:

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

# Especifique la dirección del servidor proxy con el nombre de usuario y la contraseña
proxy_address = ""
proxy_username = ""
proxy_password = ""

# Configurar las opciones de Chrome con el proxy y la autenticación
chrome_options = Options()
chrome_options.add_argument(f'--proxy-server={proxy_address}')
chrome_options.add_argument(f'--proxy-auth={proxy_username}:{proxy_password}')

# Crear una instancia WebDriver con selenium-wire
driver = wiredriver.Chrome(options=chrome_options)

youtube_url_to_scrape = ""

# Realice su automatización Selenium con las capacidades mejoradas de 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}")


# Registrar datos en 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

La salida tiene el siguiente aspecto:

Screenshot_1.png

Aprovechar de forma segura la gran cantidad de información de YouTube resulta muy beneficioso cuando se emplean scripts bien elaborados que utilizan proxies para garantizar el cumplimiento de las políticas y normativas de la plataforma. El enfoque descrito anteriormente facilita la extracción responsable de datos y mitiga el riesgo de posibles restricciones impuestas por la plataforma.

Comentarios:

0 Comentarios