Guía paso a paso para scrapear Google News con Python

Comentarios: 0

Para recopilar los titulares de las noticias más recientes, monitorizar las tendencias de las noticias y realizar análisis de sentimiento sobre temas de actualidad, el scraping de Google News demuestra ser una herramienta inestimable. En este artículo, te guiaremos a través del proceso de scraping de Google News mediante Python. Emplearemos una biblioteca de peticiones para obtener el contenido de las páginas; lxml para analizar los documentos HTML y extraer los datos necesarios. Al final de este tutorial, aprenderás a extraer titulares de noticias y sus respectivos enlaces de Google News en formato JSON estructurado.

Paso 1: Configurar el entorno

Antes de empezar, asegúrate de que tienes Python instalado en tu sistema. Puedes instalar las librerías necesarias ejecutando los siguientes comandos:

pip install requests 
pip install lxml

Estas librerías nos permitirán realizar peticiones HTTP y parsear el contenido HTML de la página web.

Paso 2: Comprender la URL de destino y la estructura XPath

Vamos a scrapear la página de Google News en la siguiente URL:

URL = "https://news.google.com/topics/CAAqKggKIiRDQkFTRlFvSUwyMHZNRGRqTVhZU0JXVnVMVWRDR2dKSlRpZ0FQAQ?hl=en-US&gl=US&ceid=US%3Aen"

Esta página contiene múltiples noticias, cada una con un titular principal y artículos relacionados. La estructura XPath para estos artículos es la siguiente:

  • Contenedor principal de noticias: //c-wiz[@jsrenderer="ARwRbe"]
  • Título de la noticia principal: //c-wiz[@jsrenderer="ARwRbe"]/c-wiz/div/article/a/text()
  • Enlace principal de noticias: //c-wiz[@jsrenderer="ARwRbe"]/c-wiz/div/article/a/@href
  • Contenedor de noticias relacionado: //c-wiz[@jsrenderer="ARwRbe"]/c-wiz/div/div/article/
  • Noticias relacionadas: //c-wiz[@jsrenderer="ARwRbe"]/c-wiz/div/div/article/a/text()
  • Enlace de noticias relacionadas: //c-wiz[@jsrenderer="ARwRbe"]/c-wiz/div/div/article/a/@href

La estructura HTML de Google Noticias se mantiene coherente en las distintas páginas, lo que garantiza que los elementos XPath especificados sean aplicables universalmente.

Paso 3: Obtención del contenido de Google Noticias

Empezaremos obteniendo el contenido de la página de Google News utilizando la biblioteca de peticiones. Aquí está el código para obtener el contenido de la página:

import requests

url = "https://news.google.com/topics/CAAqKggKIiRDQkFTRlFvSUwyMHZNRGRqTVhZU0JXVnVMVWRDR2dKSlRpZ0FQAQ?hl=en-US&gl=US&ceid=US%3Aen"
response = requests.get(url)

if response.status_code == 200:
    page_content = response.content
else:
    print(f"Failed to retrieve the page. Status code: {response.status_code}")

Este código envía una solicitud GET a la URL de Google Noticias y almacena el contenido HTML de la página en la variable page_content.

Paso 4: analizar el contenido HTML con lxml

Con el contenido HTML en la mano, podemos utilizar lxml para analizar la página y extraer los titulares de las noticias y los enlaces.

from lxml import html

# Analizar el contenido HTML
parser = html.fromstring(page_content)

Paso 5: extracción de datos de noticias

Google Noticias organiza sus artículos en contenedores específicos. Primero extraeremos estos contenedores usando su XPath y luego iteraremos a través de ellos para extraer los titulares y enlaces individuales de las noticias.

Extracción de los principales artículos de noticias

Los principales artículos de noticias se encuentran bajo el siguiente XPath:

main_news_elements = parser.xpath('//c-wiz[@jsrenderer="ARwRbe"]')

Ahora podemos recorrer los 10 primeros elementos válidos y extraer los títulos y enlaces:

news_data = []

for element in main_news_elements[:10]:
    title = element.xpath('.//c-wiz/div/article/a/text()')[0]
    link = "https://news.google.com" + element.xpath('.//c-wiz/div/article/a/@href')[0][1:]

    
    # Asegurarse de que los datos existen antes de añadirlos a la lista
    if title and link:
        news_data.append({
        "main_title": title,
        "main_link": link,
    })

Extracción de artículos relacionados dentro de cada nuevo elemento principal

El elemento principal de noticias tiene subsecciones donde hay noticias relacionadas. Podemos extraerlas utilizando un enfoque similar:

related_articles = []
related_news_elements = element.xpath('.//c-wiz/div/div/article')

for related_element in related_news_elements:
    related_title = related_element.xpath('.//a/text()')[0]
    related_link = "https://news.google.com" + related_element.xpath('.//a/@href')[0][1:]
    related_articles.append({"title": related_title, "link": related_link})

news_data.append({
    "main_title": title,
    "main_link": link,
    "related_articles": related_articles
})

Paso 6: Guardar los datos como JSON

Después de extraer los datos, podemos guardarlos en un archivo JSON para su uso posterior.

import json

with open('google_news_data.json', 'w') as f:
    json.dump(news_data, f, indent=4)

Este código creará un archivo llamado google_news_data.json que contendrá todos los titulares de noticias raspados y sus enlaces correspondientes.

Uso de proxies

Al raspar grandes cantidades de datos, especialmente de sitios con mucho tráfico como Google News, puedes encontrarte con problemas como el bloqueo de IP o la limitación de velocidad. Para evitarlo, puedes utilizar proxies. Los proxies te permiten dirigir tus solicitudes a través de diferentes direcciones IP, lo que dificulta que el sitio web detecte y bloquee tus actividades de scraping.

Para este tutorial, puedes utilizar un proxy modificando la llamada requests.get:

proxies = {
    "http": "http://your_proxy_ip:port",
    "https": "https://your_proxy_ip:port",
}

response = requests.get(url, proxies=proxies)

Si está trabajando con un proveedor de servicios que gestiona la rotación de proxy, sólo tiene que configurar el servicio en sus solicitudes. El proveedor se encargará de la rotación y la gestión del grupo de IP en su extremo.

Personalización de las cabeceras de las peticiones

A veces, los sitios web pueden bloquear las solicitudes que no tienen las cabeceras adecuadas, como una cadena de agente de usuario que identifica la solicitud como procedente de un navegador. Puedes personalizar tus cabeceras para evitar ser detectado:

headers = {
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
    'accept-language': 'en-IN,en;q=0.9',
    'cache-control': 'no-cache',
    'dnt': '1',
    'pragma': 'no-cache',
    'priority': 'u=0, i',
    'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
    'sec-ch-ua-arch': '"x86"',
    'sec-ch-ua-bitness': '"64"',
    'sec-ch-ua-full-version-list': '"Not)A;Brand";v="99.0.0.0", "Google Chrome";v="127.0.6533.72", "Chromium";v="127.0.6533.72"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-model': '""',
    'sec-ch-ua-platform': '"Linux"',
    'sec-ch-ua-platform-version': '"6.5.0"',
    'sec-ch-ua-wow64': '?0',
    'sec-fetch-dest': 'document',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-site': 'none',
    'sec-fetch-user': '?1',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
}

response = requests.get(url, headers=headers)

Ejemplo de código completo

Aquí tienes el código completo, combinando todos los pasos:

import requests
import urllib3
from lxml import html
import json

urllib3.disable_warnings()

# URL y cabeceras
url = "https://news.google.com/topics/CAAqKggKIiRDQkFTRlFvSUwyMHZNRGRqTVhZU0JXVnVMVWRDR2dKSlRpZ0FQAQ?hl=en-US&gl=US&ceid=US%3Aen"
headers = {
   'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
   'accept-language': 'en-IN,en;q=0.9',
   'cache-control': 'no-cache',
   'dnt': '1',
   'pragma': 'no-cache',
   'priority': 'u=0, i',
   'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
   'sec-ch-ua-arch': '"x86"',
   'sec-ch-ua-bitness': '"64"',
   'sec-ch-ua-full-version-list': '"Not)A;Brand";v="99.0.0.0", "Google Chrome";v="127.0.6533.72", "Chromium";v="127.0.6533.72"',
   'sec-ch-ua-mobile': '?0',
   'sec-ch-ua-model': '""',
   'sec-ch-ua-platform': '"Linux"',
   'sec-ch-ua-platform-version': '"6.5.0"',
   'sec-ch-ua-wow64': '?0',
   'sec-fetch-dest': 'document',
   'sec-fetch-mode': 'navigate',
   'sec-fetch-site': 'none',
   'sec-fetch-user': '?1',
   'upgrade-insecure-requests': '1',
   'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
}

# Configuración de proxies (sustitúyalo por los datos de su proxy)
proxy = 'ip:port'
proxies = {
   "http": f"http://{proxy}",
   "https": f"https://{proxy}",
}

# Obtener el contenido de la página con los encabezados y proxies especificados
response = requests.get(url, headers=headers, proxies=proxies, verify=False)

# Comprobar si la solicitud se ha realizado correctamente
if response.status_code == 200:
   page_content = response.content
else:
   print(f"Failed to retrieve the page. Status code: {response.status_code}")
   exit()

# Analiza el contenido HTML con lxml
parser = html.fromstring(page_content)

# Extraer las principales noticias y artículos relacionados
main_news_elements = parser.xpath('//*[@id="i10-panel"]/c-wiz/c-wiz')

# Inicializar una lista para almacenar los datos de noticias extraídos
news_data = []

# Recorrer en bucle cada elemento principal de las noticias
for element in main_news_elements[:10]:
   # Extraer el título y el enlace de la noticia principal
   title = element.xpath('.//c-wiz/div/article/a/text()')
   link = element.xpath('.//c-wiz/div/article/a/@href')

   # Inicializar una lista para almacenar los artículos relacionados con esta noticia principal
   related_articles = []

   # Extraer elementos de noticias relacionados dentro del mismo bloque
   related_news_elements = element.xpath('.//c-wiz/div/div/article')

   # Recorre en bucle cada elemento de noticia relacionado y extrae el título y el enlace
   for related_element in related_news_elements:
       related_title = related_element.xpath('.//a/text()')[0]
       related_link = "https://news.google.com" + related_element.xpath('.//a/@href')[0][1:]
       related_articles.append({"title": related_title, "link": related_link})

   # Añade la noticia principal y sus artículos relacionados a la lista news_data
   if title is not None:
       news_data.append({
           "main_title": title,
           "main_link": f'https://news.google.com{link}',
           "related_articles": related_articles
       })
   else:
       continue


# Guardar los datos extraídos en un archivo JSON
with open("google_news_data.json", "w") as json_file:
   json.dump(news_data, json_file, indent=4)

print('Data extraction complete. Saved to google_news_data.json')

El scraping de Google News mediante Python, junto con las bibliotecas requests y lxml, facilita el análisis detallado de las tendencias de las noticias. Implementar proxies y configurar las cabeceras de las peticiones es crucial para evitar bloqueos y mantener la estabilidad del scraper. Los proxies ideales para este fin incluyen proxies de centros de datos IPv4 e IPv6 y proxies ISP, que ofrecen altas velocidades y bajo ping. Además, los proxies residenciales dinámicos son muy eficaces debido a su factor de confianza superior.

Comentarios:

0 Comentarios