Guía definitiva para crear un raspador web con Pyppeteer
Mihnea-Octavian Manolache el 28 de febrero de 2023

Cuando se trata de Python y automatización web, Selenium era más o menos el go-to. Al menos hasta ahora. Debido al éxito de Puppeteer en la comunidad de JavaScript, los desarrolladores de Python comenzaron a mirar más y más en él. Y así es como Pyppeteer llegó a existir. Pero, ¿qué es exactamente Pyppeteer? ¿Y por qué deberíamos elegirlo en lugar de Selenium? ¿Es lo suficientemente fiable como para construir una solución compleja con él? A todas estas preguntas y muchas más, daremos respuesta en el artículo de hoy. Mi objetivo para hoy es, que si lees este material, te vayas con al menos:
- Una definición de Pyppeteer y sus casos de uso
- Comprensión de cómo Pyppeteer se compara con Selenium
- Implementación real de un raspador web con Pyppeteer
Así que prepárate, porque hoy hablaremos y nos pondremos manos a la obra con la codificación.
¿Qué es realmente Pyppeteer y cómo se utiliza?
Si estás leyendo esto, lo más probable es que ya estés familiarizado con lo que es el scripting web en general. Y probablemente ya hayas oído hablar de Puppeteer o Selenium, dependiendo de tu lenguaje de programación favorito. Pero Pyppeteer es de hecho más nuevo en la escena del web scraping. Bueno, para abreviar, Pyppeteer es mucho más parecido a Puppeteer que a Selenium.
Puppeteer es una librería Node.js que facilita el control de una versión headless de Chrome a través del protocolo DevTools. Pyppeteer es un puerto Python de Puppeteer. Al igual que el Puppeteer original, Pyppeteer es una librería, escrita en Python, que básicamente automatiza un navegador. En otras palabras, Pyppeteer es una implementación en Python de la API de Puppeteer, que permite utilizar las características de Puppeteer en un entorno Python. La principal diferencia entre los dos es el lenguaje utilizado.
Terminología de Pyppeteer que debe conocer
Antes de seguir adelante, creo que deberíamos discutir algunos términos de uso común en el contexto de Pyppeteer:
- Headless: Significa iniciar un navegador sin interfaz gráfica de usuario (GUI). En otras palabras, se ejecuta "entre bastidores" y no se ve en la pantalla. Suele utilizarse para reducir el uso de recursos durante el scraping.
- Con cabeza: Por el contrario, un navegador "headful" es aquel que funciona con una interfaz gráfica de usuario. Es lo contrario de un navegador "headless" y suele utilizarse para probar, depurar o interactuar manualmente con páginas web.
- Contexto del navegador: Es un estado compartido entre todas las páginas de un navegador. Suele utilizarse para establecer la configuración de todo el navegador, como las cookies, las cabeceras HTTP y la geolocalización.
- DOM: El Modelo de Objetos del Documento (DOM) es una interfaz de programación para documentos HTML y XML. Representa la estructura de una página web en forma de árbol, con nodos que representan elementos. Pyppeteer permite interactuar con los elementos de una página manipulando el DOM.
- Elementos: Los componentes básicos de una página web. Se definen mediante etiquetas, atributos y valores.
Por supuesto, hay más cosas y aprenderás más por el camino. Pero quería que te hicieras una idea para que tengamos un comienzo sólido. Estoy convencido de que conocer estos términos te ayudará a comprender mejor la esencia de este artículo.
¿Por qué utilizar Pyppeteer en su proyecto de scraping?
Creo que hay dos aspectos en este asunto. El primero es por qué Pyppeteer es una buena opción para el web scraping en general. El segundo es por qué usar Pyppeteer en lugar de Selenium. En general, algunas de las ventajas de Pyppeteer dan cuenta de:
- Evaluación de JavaScript: Pyppeteer proporciona una función `page.evaluate()`. Permite ejecutar código JavaScript dentro del contexto de la página.
- Control de red: Pyppeteer proporciona un método `page.on()`. Esto te permite escuchar eventos de red, como peticiones y respuestas, que ocurren en una página.
- Rastreo y registro: Pyppeteer permite rastrear la actividad del navegador y registrar los mensajes del navegador desde una página. Esto hace que sea fácil de depurar, rastrear y entender lo que un sitio web está haciendo.
Comparado con Selenium, es bastante similar, en el sentido de que ambos se utilizan para automatizar un navegador web. Sin embargo, hay algunas diferencias y ventajas clave que Pyppeteer tiene sobre Selenium:
- Simplicidad: Pyppeteer tiene una API más simple y consistente que Selenium, lo que hace que sea más fácil de usar para los principiantes. La API de Pyppeteer está construida sobre el protocolo DevTools, que está cerca del navegador y es fácil de aprender y usar.
- Rendimiento: Pyppeteer puede ser más rápido que Selenium porque está construido sobre el protocolo DevTools. El protocolo está diseñado para depurar páginas web y es mucho más rápido que Selenium WebDriver.
- Mejor control de la red: Pyppeteer permite un mayor control sobre la configuración de red del navegador, como la interceptación de peticiones y el bloqueo de peticiones/respuestas. Esto hace que sea más fácil de probar y diagnosticar problemas relacionados con la red.
Y, por supuesto, también hay una cuestión de elección. Yo, por ejemplo. En el día a día, codifico en JavaScript. Y estoy bastante familiarizado con Puppeteer. Pero mi lenguaje de programación favorito es Python. Así que si tuviera que construir un Scraper con una tecnología conocida en un lenguaje que prefiero, probablemente iría con Pyppeteer.
Y dicho esto, creo que hemos cubierto los aspectos "hablados" de este artículo. Es hora de empezar con algo de codificación real.
Cómo crear un raspador web con Pyppeteer
Antes de empezar a programar, déjame presentarte la documentación oficial de Pyppeteer. Soy partidario de utilizar la documentación oficial siempre que uno se sienta atascado. Eso es antes de hacer preguntas en la comunidad (como en Stackoverflow). Normalmente encuentro que la mayoría de las respuestas se pueden encontrar si lees primero la documentación. Así que toma esto como una amable petición de mi parte. Siempre que estés atascado, consulta la documentación, luego busca las respuestas y sólo haz preguntas como último recurso.
#1: Configurar el entorno
Lo primero es lo primero, como desarrollador de Python, probablemente estés familiarizado con los entornos virtuales. Así que lo primero que tenemos que hacer es crear un entorno virtual para nuestro proyecto. Esta es generalmente la secuencia de comandos que utilizo:
# Crear un nuevo directorio y navegar en él
~ " mkdir py_project && cd py_project
# Crear el entorno virtual
~ " python3 -m venv env
# Activar el entorno virtual
~ " source env/bin/activate
Con respecto al entorno virtual, ya está todo listo. Es hora de seguir adelante e instalar Pyppeteer. Ya que tienes tu terminal abierta, simplemente escribe:
# Instale el paquete usando pip
~ " python3 -m pip install pyppeteer
# Abra el proyecto en su IDE
~ " code .
#2: Crear un raspador Pyppeteer sencillo
El último comando abre Visual Studio Code o tu IDE preferido. Así que ahora que estás en el "entorno de desarrollo", vamos a crear un nuevo archivo `.py` que contendrá nuestro código. Llamaré a mi archivo `scraper.py`. Ten en cuenta que Pyppeteer soporta de forma nativa la ejecución asíncrona. Así que vamos a importar tanto `asyncio` como `pyppeteer` en nuestro archivo:
import asyncio
from pyppeteer import launch
Una vez hecho esto, podemos pasar a código más complicado. En general, no soy el mayor defensor de la programación funcional. Sin embargo, creo que dividir el código en pequeños trozos permite un mejor aprendizaje. Así que vamos a envolver nuestro código dentro de una función:
async def scrape(url):
browser = await launch()
page = await browser.newPage()
await page.goto(url)
content = await page.content()
await browser.close()
return content

Esta función toma una URL como entrada y lanza un navegador headless usando pyppeteer. Luego, navega a la URL proporcionada, recupera el contenido de la página y cierra el navegador. El valor que devuelve no es más que el HTML recogido de la página. Puedes utilizar esta función para scrapear casi cualquier sitio web. Para usar la función, debes llamarla en un bucle de eventos `asyncio`, como este:
async def main():
content = await scrape('https://www.example.com')
print(content)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
#3: Añadir más funcionalidad
Hasta ahora, tenemos un rascador que funciona. Pero eso es prácticamente todo lo que tenemos. Si quieres construir un raspador web más avanzado con Pyppeteer, tendrás que añadir más funcionalidad a id. Alerta de spoiler: nos sumergiremos en el mundo de la programación orientada a objetos. Pero primero, tracemos nuestros objetivos. ¿Qué queremos que nuestro scraper sea capaz de hacer?
- Inicializar el navegador con algunos valores personalizados
- Navegar y extraer contenidos de una página web
- Escribir texto en un campo de entrada
- Extraer el valor de un solo elemento
- Extraer valor de varios elementos
3.1. Opciones personalizadas
Así que vamos a crear una nueva clase `Scraper` por ahora y vamos a añadir sus métodos después:
class Scraper:
def __init__(self, launch_options: dict) -> None:
self.options = launch_options['options']
self.viewPort = launch_options['viewPort'] if 'viewPort' in launch_options else None
pass
El único argumento que estamos utilizando para nuestro Scraper es un diccionario `launch_options`. Como ves, contiene dos claves. Una de ellas define las opciones del lanzador de Pyppeteer. La segunda opción es `None` o un diccionario que contiene el `width` y el `height` del `viewPort`. Esta última se utiliza para este método.
3.2. Navegar a una página
Si nos fijamos en la función que utilizamos antes, veremos que cubrimos tanto la navegación como la extracción de datos en bruto de una URL específica. Lo único que tenemos que hacer es retocar y convertir la función en un método para nuestro Scraper:
async def goto(self, url: str) -> None:
self.browser = await launch(options=self.options)
self.page = await self.browser.newPage()
await self.page.setViewport(self.viewPort) if self.viewPort != None else print('[i] Using default viewport')
await self.page.goto(url)
Este método es bastante sencillo. Primero, lanza un nuevo navegador, con las opciones personalizadas que establecimos antes. Luego crea una nueva página y, si nuestro diccionario `launch_options` tiene `viewPort`, entonces establece el viewPort de la página. Si no, registra un simple mensaje. Por último, pero no menos importante, nos lleva al objetivo.
3.3. Extraer datos brutos de una página
De nuevo, tenemos el método en nuestra función `scraper` inicial. Sólo esperaremos a que la `page.content()` se cargue y devuelva su valor:
async def get_full_content(self) -> str:
content = await self.page.content()
return content
3.4. Escribir texto en un campo de entrada
Para escribir algo en un campo de entrada usando Pyppeteer, necesitarás dos cosas. Primero, localizar el elemento. Segundo, añadirle algún valor. Afortunadamente, Pyppeteer tiene métodos para ambas acciones:
async def tipo_valor(self, selector: str, valor: str) -> None:
element = await self.page.querySelector(selector)
await elemento.tipo(valor)
3.5. Extraer valor de la página
Recuerda que queremos poder extraer el valor de un único elemento o valores de múltiples elementos. Podríamos utilizar un único método para ambos. Pero normalmente me gustan las cosas separadas. Así que por ahora, voy a añadir dos métodos más:
async def extract_one(self, selector) -> str:
element = await self.page.querySelector(selector)
text = await element.getProperty("textContent")
return await text.jsonValue()
Aquí, estamos localizando el elemento usando el método `querySelector`. Luego esperamos el `textContent` y devolvemos su `jsonValue()`. Por otro lado, cuando queramos seleccionar muchos elementos, usaremos `querySelector`:
async def extract_many(self, selector) -> list:
result = []
elements = await self.page.querySelectorAll(selector)
for element in elements:
text = await element.getProperty("textContent")
result.append(await text.jsonValue())
return result
Este método funciona de forma similar a `extract_one`. La única diferencia es su valor de retorno. Esta vez estamos devolviendo una lista de todo el texto dentro de los elementos seleccionados. Y supongo que con esto añadido, hemos tocado todos nuestros objetivos.
#4: Hazlo sigiloso
En el raspado web, el sigilo puede describirse como la capacidad de pasar desapercibido. Por supuesto, construir un scraper totalmente indetectable requiere mucho trabajo. Por ejemplo, el Modo Sigiloso de Web Scraping API es mantenido por un equipo dedicado. Y el esfuerzo realizado hace que la huella digital de nuestro scraper sea única en cada solicitud.
Pero mi objetivo general para este tutorial es ponerte en el camino correcto. Y el camino correcto para un web scraper completo con Pyppeteer implica añadirle alguna funcionalidad stealth. Por suerte, al igual que existe `puppeteer-extra-plugin-stealth` en Node, también hay un paquete para Python. Y se llama intuitivamente, `pyppeteer-stealth`. Para añadirlo a tu proyecto, primero, instálalo usando pip:
~ " python3 -m pip install pyppeteer_stealth
A continuación, impórtalo en tu proyecto y sólo tienes que añadir una línea de código adicional:
async def goto(self, url: str) -> None:
self.browser = await launch(options=self.options)
self.page = await self.browser.newPage()
# Make it stealthy
await stealth(self.page)
await self.page.setViewport(self.viewPort) if self.viewPort != None else print('[i] Using default viewport')
await self.page.goto(url)
Y así es como se ejecuta el scraper. He añadido algunos comentarios al código para resaltar lo que hace cada paso:
async def main():
# Define the launch options dictionary
launch_options = {
'options': {
'headless': False,
'autoClose': True
},
'viewPort': {
'width': 1600,
'height': 900
}
}
# Initialize a new scraper
scraper = Scraper(launch_options)
# Navigae to your target
await scraper.goto('https://russmaxdesign.github.io/accessible-forms/accessible-name-input01.html')
# Type `This is me` inside the input field
await scraper.type_value(
'#fish',
'This is me')
# Scrape the entire page
content = await scraper.get_full_content()
print(content)
# Scrape one single element
el = await scraper.extract_one('body > div:nth-child(14) > ul')
print(el)
# Scrape multiple elements
els = await scraper.extract_many('p')
print(els)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Conclusión
Pyppeteer es una herramienta asombrosa para el web scraping. Adapta toda la API de Puppeteer a Python, haciendo posible que la comunidad Python utilice esta tecnología sin tener que aprender JavaScript. Por otra parte, no creo que sea un reemplazo para Selenium, pero seguro que es una buena alternativa a ella.
Espero que el artículo de hoy haya aportado valor a tu aprendizaje. Y como me gusta superar los límites de todos, te reto a que añadas más a lo que has aprendido hoy. El scraper que construimos juntos es un muy buen punto de partida e introduce un elemento clave en la programación: OOP. Así que te reto a que añadas más métodos a `Scraper` y lo hagas realmente asombroso.
Noticias y actualidad
Manténgase al día de las últimas guías y noticias sobre raspado web suscribiéndose a nuestro boletín.
We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Artículos relacionados

Recopile sin esfuerzo datos en tiempo real de los motores de búsqueda mediante la API SERP Scraping. Mejore el análisis de mercado, el SEO y la investigación temática con facilidad. ¡Empiece hoy mismo!


Explore las complejidades del scraping de datos de productos de Amazon con nuestra guía en profundidad. Desde las mejores prácticas y herramientas como Amazon Scraper API hasta las consideraciones legales, aprenda a superar los desafíos, eludir los CAPTCHA y extraer información valiosa de forma eficiente.


Conozca cuál es el mejor navegador para eludir los sistemas de detección de Cloudflare mientras hace web scraping con Selenium.
