Volver al blog
Guías
Mihnea-Octavian ManolacheLast updated on May 12, 202612 min read

Cómo crear un raspador web con Pyppeteer (Guía 2026)

Cómo crear un raspador web con Pyppeteer (Guía 2026)
En resumen: Pyppeteer es la adaptación no oficial de Puppeteer para Python y sigue funcionando para controlar un Chromium real desde asyncio. En esta guía lo instalarás y escribirás un moderno rastreador web con Pyppeteer utilizando asyncio.run y try/finally, gestionar esperas, formularios, capturas de pantalla, desplazamiento infinito, cookies y proxies, y aprenderás cuándo migrar a Playwright, Selenium o una API de scraping alojada.

Si ya has superado requests y BeautifulSoup porque los datos que necesitas solo aparecen después de que se ejecute JavaScript, probablemente ya hayas pensado en crear un scraper web con Pyppeteer. Pyppeteer es la adaptación a Python de Puppeteer, y te permite iniciar una instancia real de Chromium, esperar a que se activen selectores, hacer clic en botones y ejecutar código JavaScript arbitrario dentro de la página desde async código Python. Eso es suficiente para extraer aplicaciones de una sola página, feeds de desplazamiento infinito, interfaces de búsqueda y cualquier otra cosa que se oculte tras una fetch llamada.

Esta guía está dirigida a desarrolladores de Python de nivel intermedio en 2026. Abordaremos un análisis honesto del estado del proyecto, una comparación con Selenium, Playwright y Node Puppeteer, patrones asíncronos modernos (asyncio.run, try/finally, esperas estructuradas) y un ejemplo completo de extremo a extremo que recorre múltiples palabras clave en una interfaz de usuario de búsqueda basada en JavaScript. Al final, tendrás una plantilla de scraper de Pyppeteer en funcionamiento, además de un marco de decisión claro para saber cuándo Pyppeteer es la herramienta adecuada y cuándo no.

Pyppeteer en 2026: dónde encaja y qué ha cambiado

Pyppeteer es, en esencia, una envoltura de Python que refleja la API de Puppeteer: launch un navegador, abrir una page, llamar waitForSelector, ejecutar evaluate, repetir. El modelo mental se corresponde uno a uno con el proyecto original de Puppeteer en GitHub, lo cual resulta útil si alguna vez has leído un tutorial de Node y querías seguir en Python.

La advertencia sincera para 2026 es que Pyppeteer solo recibe un mantenimiento mínimo. Los mantenedores indican en el archivo README del proyecto que el mantenimiento es mínimo, y varias de las funciones más recientes de Puppeteer nunca se han portado. Eso no significa que tu rastreador vaya a dejar de funcionar mañana, pero sí significa que no deberías elegir Pyppeteer para un sistema de producción de larga duración sin considerar Playwright y una API de rastreo gestionada como alternativas. Volveremos a esa decisión al final.

Pyppeteer frente a Selenium, Playwright y Puppeteer

Antes de decidirte, es útil comparar Pyppeteer con sus alternativas más cercanas. La tabla siguiente es una guía rápida para que puedas elegir la herramienta adecuada para tu pila, en lugar de quedarte con la primera que aparezca en Google.

Herramienta

Lenguaje

Modelo asíncrono

Navegadores

Opciones de ocultación

Mantenimiento

Pyppeteer

Python

Nativo asyncio

Chromium

Manual, sin complemento nativo

Mantenimiento mínimo

Playwright (Python)

Python

Sincronización + asyncio

Chromium, Firefox, WebKit

Configuración predeterminada integrada para un uso discreto

Desarrollado activamente por Microsoft

Selenium

Python (y otros)

Sincronización (asíncrona a través de envoltorios)

Chromium, Firefox, Edge, Safari

selenium-stealth, controladores no detectados

Mantenido activamente, maduro

Puppeteer (Node)

JavaScript / TypeScript

Promesas nativas

Chromium, Firefox (experimental)

puppeteer-extra-plugin-stealth

Desarrollado activamente por el equipo de Chrome

Consejo práctico: elige Puppeteer en Node si quieres las funciones más recientes, Playwright para nuevos proyectos en Python que necesiten un scraping estable en distintos navegadores, Selenium cuando debas admitir Safari o flujos heredados al estilo IE, y Pyppeteer cuando un pequeño script en Python o una base de código existente ya lo admita asyncio. Para una comparación más amplia, consulta nuestros resúmenes sobre bibliotecas de navegadores sin interfaz gráfica para Python y alternativas a Puppeteer.

Configuración de Pyppeteer (correcciones para Python, Chromium y M1/M2)

Utiliza Python 3.10 o una versión más reciente y un entorno virtual. Con uv, la instalación se realiza con una sola línea:

uv init pyppeteer-demo && cd pyppeteer-demo
uv add pyppeteer
uv run pyppeteer-install   # downloads bundled Chromium

Si prefieres pip, sustituye python -m venv .venv && pip install pyppeteer && pyppeteer-install. En la primera ejecución, Pyppeteer puede descargar una versión empaquetada de Chromium (alrededor de 150 MB en el momento de escribir este artículo, así que vuelve a consultar las últimas notas de la versión antes de lanzarla). Para omitir esa descarga y utilizar el Chromium del sistema, configura PYPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 y pasa executablePath a launch:

# macOS:  /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
# Linux:  /usr/bin/google-chrome
# Windows: C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe
await launch(executablePath='/usr/bin/google-chrome', headless=True)

Truco para Mac M1/M2: Pyppeteer puede ser un poco delicado en arm64. Si Chromium se niega a iniciarse o se bloquea inmediatamente, vuelve a ejecutar el terminal bajo Rosetta y la instalación suele completarse sin problemas.

Crea un rastreador web mínimo con Pyppeteer: una plantilla moderna

Aquí tienes un punto de partida reutilizable para un rastreador web con Pyppeteer que utiliza asyncio.run, envuelve el navegador en try/finally, y pasa el HTML renderizado a BeautifulSoup. Vamos a extraer quotes.toscrape.com/js/, una página de prueba que renderiza citas mediante JavaScript, de modo que los clientes HTTP simples ven una página vacía <body>.

import asyncio
from bs4 import BeautifulSoup
from pyppeteer import launch

URL = 'https://quotes.toscrape.com/js/'

async def scrape() -> list[dict]:
    browser = await launch(headless=True, args=['--no-sandbox'])
    try:
        page = await browser.newPage()
        await page.goto(URL, {'waitUntil': 'networkidle2'})
        await page.waitForSelector('.quote')
        html = await page.content()
        soup = BeautifulSoup(html, 'html.parser')
        return [
            {
                'text': q.select_one('.text').get_text(strip=True),
                'author': q.select_one('.author').get_text(strip=True),
            }
            for q in soup.select('.quote')
        ]
    finally:
        await browser.close()

if __name__ == '__main__':
    for row in asyncio.run(scrape()):
        print(row)

Hay tres cosas importantes aquí. asyncio.run sustituye el antiguo get_event_loop().run_until_complete que aún muestran los tutoriales más antiguos. try/finally garantiza que Chromium se cierre incluso cuando tu código genere un error. Y waitForSelector es el punto de sincronización explícito, no un sleep que hace perder tiempo en páginas rápidas y agota el tiempo de espera en las lentas.

Esperar a los elementos de la forma correcta

Pyppeteer ofrece varias opciones de espera, y la elección es importante. waitFor() es imprecisa y propensa a errores porque espera a que ocurra «algo», mientras que waitForSelector() es explícito y se resuelve solo cuando el nodo de destino existe en el DOM. Opta por waitForNavigation después de un envío, y utiliza waitUntil='networkidle2' cuando la página emita llamadas en segundo plano fetch . Como alternativa, puedes llamar a page.waitFor(5000) para hacer una pausa de cinco segundos, pero considera cualquier espera de tiempo fijo como último recurso, ya que es la principal causa de scrapers inestables.

Hacer clic, escribir y enviar formularios

Para interacciones más complejas, combina page.click, page.type (con un pequeño delay para que parezca más humano) y page.keyboard.press. Tras enviar un formulario, espera la navegación en paralelo con el clic para que no se pase por alto el cambio de URL:

await page.type('input[name="q"]', 'pyppeteer', {'delay': 80})
await asyncio.gather(
    page.waitForNavigation({'waitUntil': 'networkidle2'}),
    page.keyboard.press('Enter'),
)

Ese patrón funciona para formularios de inicio de sesión, barras de búsqueda y cualquier interfaz de usuario en la que un POST active una redirección.

Las capturas de pantalla y las exportaciones a PDF

page.screenshot() capturan la ventana de visualización visible de forma predeterminada. Pasa fullPage=True para capturas de arriba abajo, y llama setViewport primero si quieres una resolución específica. Los PDF provienen de page.pdf() y funcionan mejor en páginas con estilos de impresión limpios:

await page.setViewport({'width': 1440, 'height': 900})
await page.screenshot({'path': 'page.png', 'fullPage': True})
await page.pdf({
    'path': 'page.pdf',
    'format': 'A4',
    'printBackground': True,
    'margin': {'top': '20mm', 'bottom': '20mm', 'left': '15mm', 'right': '15mm'},
})

Manejo del desplazamiento infinito y la carga diferida

Las páginas con desplazamiento infinito solo renderizan el siguiente lote cuando se desplaza la ventana de visualización hacia abajo. Utiliza page.evaluate para ejecutar un pequeño bucle JS que supervise document.body.scrollHeight y se detiene cuando deja de crecer:

await page.evaluate('''async () => {
  await new Promise(resolve => {
    let last = 0;
    const timer = setInterval(() => {
      window.scrollBy(0, 800);
      const h = document.body.scrollHeight;
      if (h === last) { clearInterval(timer); resolve(); }
      last = h;
    }, 400);
  });
}''')

Limita el bucle con un recuento máximo de iteraciones si el feed es realmente infinito.

Gestión de cookies, sesiones y agentes de usuario

Para las páginas con inicio de sesión, inicia sesión una vez, guarda las cookies y vuelve a utilizarlas en la siguiente ejecución para no tener que volver a iniciar sesión:

cookies = await page.cookies()          # save somewhere safe
await page.setCookie(*saved_cookies)    # restore later
await page.setUserAgent('Mozilla/5.0 ... Chrome/124 Safari/537.36')
await page.setViewport({'width': 1366, 'height': 768})

Empareja setUserAgent con una setViewport para que la huella digital del dispositivo se mantenga internamente coherente. Un agente de usuario de escritorio con una ventana de visualización de 320 píxeles es un indicio clásico para la detección de bots.

Scraper web de extremo a extremo con Pyppeteer: scraping de una interfaz de búsqueda basada en JavaScript

Juntemos todo. El script siguiente recorre varias palabras clave, escribe cada una en una barra de búsqueda que muestra los resultados en el lado del cliente, espera a que aparezcan las tarjetas de resultados, extrae sus títulos con querySelectorAllEval, y borra el campo de entrada antes de la siguiente palabra clave. Cambia la URL y los selectores para adaptarlos a tu objetivo real.

import asyncio
from pyppeteer import launch

KEYWORDS = ['python', 'pyppeteer', 'asyncio']
SEARCH_URL = 'https://example.com/search'   # JS-rendered UI

async def search_one(page, keyword: str) -> list[str]:
    await page.click('input[name="q"]', {'clickCount': 3})
    await page.keyboard.press('Backspace')
    await page.type('input[name="q"]', keyword, {'delay': 60})
    await page.keyboard.press('Enter')
    await page.waitForSelector('.result-card', {'timeout': 10000})
    return await page.querySelectorAllEval(
        '.result-card h3',
        '(nodes) => nodes.map(n => n.innerText.trim())',
    )

async def main():
    browser = await launch(headless=True, args=['--no-sandbox'])
    try:
        page = await browser.newPage()
        await page.goto(SEARCH_URL, {'waitUntil': 'networkidle2'})
        results = {}
        for kw in KEYWORDS:
            results[kw] = await search_one(page, kw)
            await asyncio.sleep(2)   # be polite
        return results
    finally:
        await browser.close()

if __name__ == '__main__':
    print(asyncio.run(main()))

Este patrón permite dos mejoras. En primer lugar, reutilizas un navegador y una página para todas las palabras clave, lo que reduce el coste de la ejecución. En segundo lugar, el uso explícito de waitForSelector hace que el rastreador sea resistente a las fluctuaciones de la red, por lo que el proceso no se colapsa en el momento en que una solicitud tarda 600 ms en lugar de 200 ms. A partir de aquí, añadir reintentos y concurrencia con asyncio.gather es el siguiente paso lógico.

Uso de proxies y rotación con Pyppeteer

Pyppeteer gestiona la automatización del navegador a la perfección, pero no gestiona los proxies por sí solo, por lo que hay que configurarlos al inicio. El --proxy-server indicador de Chromium acepta un único punto final, y page.authenticate añade las credenciales antes de la primera solicitud:

import random
from pyppeteer import launch

PROXIES = [
    'http://user:pass@proxy-a.example.com:8000',
    'http://user:pass@proxy-b.example.com:8000',
    'http://user:pass@proxy-c.example.com:8000',
]

async def launch_with_proxy():
    proxy = random.choice(PROXIES)   # naive rotation
    host = proxy.split('@')[-1]
    browser = await launch(args=[f'--proxy-server=http://{host}'])
    page = await browser.newPage()
    await page.authenticate({'username': 'user', 'password': 'pass'})
    return browser, page

Incluso con un proxy limpio, acabará alcanzando los límites de velocidad, así que rote por sesión o por palabra clave. Para un patrón más detallado, consulte nuestra guía de rotación de proxies en Python. Si gestionar los grupos usted mismo le parece una tarea tediosa, un producto de proxies residenciales gestionados, o una API a nivel de solicitud como la WebScrapingAPI Scraper API, le liberará de ese trabajo.

Lista de verificación de sigilo y higiene de huellas digitales

Pyppeteer no incluye un complemento de ocultación nativo, por lo que tendrás que reforzar el navegador tú mismo. Lista de verificación mínima viable:

  • Establece un agente de usuario de escritorio realista y actualizado con page.setUserAgent.
  • Combínalo con un área de visualización plausible mediante page.setViewport (1366x768 o 1440x900 son valores predeterminados seguros).
  • Aplica el navigator.webdriver indicador en un evaluateOnNewDocument gancho para que devuelva undefined en lugar de true.
  • Mantén una buena higiene de cookies: borra las cookies entre sesiones o alterna las sesiones cuando cambies de IP.
  • Rota las IP a través de proxies residenciales o móviles para cualquier objetivo con defensas serias contra bots.
  • Limita las solicitudes y humaniza los tiempos con delay en type y pequeños asyncio.sleep intervalos entre acciones.

Mejores prácticas de nivel de producción para 2026

Si quieres un rastreador web con Pyppeteer que resista un calendario real, sigue estas reglas:

  • Ejecuta el punto de entrada con asyncio.run(main()). Olvídate get_event_loop() y loop.run_until_complete(); la función moderna es más limpia y tiene menos errores.
  • Envuelve cada navegador en try/finally para que el proceso de Chromium se cierre incluso cuando tu código genere un error. Los navegadores con fugas de memoria son la causa principal de que los ejecutores de CI dejen de funcionar.
  • Prefiere waitForSelector (explícito) en lugar de waitFor (vago). Reserva los tiempos de espera fijos solo para los retrasos antibots documentados.
  • Limita el tráfico con tacto. Respeta robots.txt, limita el alcance a los datos públicos y añade fluctuación para que las 100 solicitudes no lleguen en 100 milisegundos.
  • Añade un registro estructurado (una línea JSON por página) y captura la URL, el estado, el tiempo de respuesta y el recuento de coincidencias del selector. Te lo agradecerás la primera vez que un sitio de destino cambie su HTML.

Cuándo Pyppeteer no es la herramienta adecuada (y qué usar en su lugar)

Pyppeteer es ideal para scripts puntuales, automatización interna y pequeños códigos base de Python que ya utilizan asyncio. Empieza a mostrar su antigüedad cuando necesitas cobertura entre navegadores, nuevas funciones de CDP, sigilo oficial o concurrencia a gran escala. Utiliza esta regla de decisión aproximada:

  • Quédate con Pyppeteer para prototipos, rastreadores de fin de semana y scripts de menos de unos cientos de páginas al día.
  • Pásate a Playwright (Python) cuando necesites Firefox o WebKit, una espera automática robusta o un rastreo de primera clase.
  • Pásate a Selenium si necesitas compatibilidad con Safari o integrarte en una red de pruebas existente.
  • Utiliza una API de scraping alojada cuando dediques más tiempo a la rotación de proxies, los CAPTCHAs y la infraestructura sin interfaz gráfica que a los datos reales.

Conclusiones clave

  • Pyppeteer es la versión para Python de Puppeteer, con un mantenimiento mínimo; seguirá funcionando en 2026 para asyncio, pero no es la elección adecuada para sistemas de producción de larga duración sin un plan de respaldo.
  • Utiliza asyncio.run, try/finally, y waitForSelector en lugar de los antiguos patrones de bucle de eventos y waitFor patrones que se muestran en tutoriales obsoletos.
  • Un scraper web completo con Pyppeteer cubre esperas, entradas en formularios, capturas de pantalla, PDF, desplazamiento infinito, reutilización de cookies y proxies, no solo goto.
  • No hay ningún complemento de ocultación nativo, por lo que el agente de usuario, la ventana de visualización, navigator.webdriverla gestión de cookies y la rotación de IP son responsabilidad tuya.
  • Elige Playwright, Selenium o una API de scraping gestionada en cuanto tu scraper supere los límites de una sola máquina, un solo navegador o un solo proxy.

Preguntas frecuentes

¿Se sigue manteniendo Pyppeteer y es seguro utilizarlo en 2026?

En realidad, no. Los mantenedores indican explícitamente en el archivo README del proyecto en GitHub que Pyppeteer recibe un mantenimiento mínimo y que las nuevas funciones de Puppeteer rara vez se incorporan. Sigue funcionando y sigue realizando scraping, pero para un sistema de producción de larga duración deberías evaluar Playwright (Python) o una API de scraping alojada como alternativa de desarrollo más activo antes de comprometerte.

¿Cuál es la diferencia entre Pyppeteer y Puppeteer?

Puppeteer es la biblioteca oficial de Node.js del equipo de Chrome para automatizar Chromium. Pyppeteer es una adaptación no oficial a Python que refleja la mayor parte de la API de Puppeteer, pero utiliza asyncio en lugar de Promises. Pyppeteer suele ir por detrás de Puppeteer en cuanto a nuevas funciones, y algunas API de Puppeteer faltan por completo, por lo que los ecosistemas son similares en forma pero no en superficie.

¿Debería elegir Pyppeteer, Playwright o Selenium para un nuevo proyecto de scraping en Python?

Para un nuevo proyecto en 2026, opta por Playwright en Python. Se desarrolla activamente, es compatible con Chromium, Firefox y WebKit, e incluye una función de espera automática que elimina gran parte de la inestabilidad. Elige Selenium si necesitas Safari o una red de pruebas ya existente. Elige Pyppeteer solo cuando estés ampliando un script heredado que ya lo utilice.

¿Puede Pyppeteer eludir Cloudflare, la detección de bots o los CAPTCHAs por sí solo?

No. Pyppeteer no incluye un complemento de ocultación y no tiene un solucionador de CAPTCHAs integrado. Puedes reducir tu huella digital manualmente configurando un agente de usuario realista, aplicando parches navigator.webdrivery rotando las IP residenciales, pero superar de forma fiable los retos modernos de Cloudflare o hCaptcha suele requerir un marco reforzado o una API de scraping a nivel de solicitud que se encargue del desbloqueo por ti.

¿Por qué se cuelga mi script de Pyppeteer en Macs M1 o M2?

El Chromium incluido es sensible en Apple Silicon. La solución más común es reiniciar el terminal bajo Rosetta y volver a ejecutar pyppeteer-install, lo que permite que la versión x86_64 de Chromium se instale y se inicie correctamente. Como alternativa, configura PYPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 y apunta executablePath hacia un arm64Google Chrome nativo que ya tengas instalado.

Conclusión

Crear un rastreador web con Pyppeteer en 2026 sigue siendo una opción razonable cuando se busca un script de Python pequeño y compatible con la asincronía que controle un Chromium real. Dispones de una plantilla de inicio funcional, patrones para esperas, formularios, capturas de pantalla, desplazamiento infinito, cookies y proxies, además de una lista de verificación de sigilo y una idea clara de cuándo dar el salto a Playwright, Selenium o una alternativa gestionada.

La conclusión sincera: el escaso mantenimiento de Pyppeteer significa que debes tratarlo como una herramienta táctica en lugar de como una plataforma a largo plazo. Envuelve tu navegador en try/finally, da preferencia waitForSelector a los tiempos de espera fijos y reserva presupuesto para una ruta de migración para el día en que un sitio de destino actualice sus defensas contra bots más rápido de lo que Pyppeteer implementa la siguiente función de CDP.

Si la rotación de proxies, los CAPTCHAs o las actualizaciones de Chromium empiezan a consumir más tiempo que el propio scraping, pasa la capa de solicitudes a la API Scraper de WebScrapingAPI y mantén tu código de Pyppeteer centrado en analizar los datos que realmente te interesan.

Acerca del autor
Mihnea-Octavian Manolache, Desarrollador Full Stack @ WebScrapingAPI
Mihnea-Octavian ManolacheDesarrollador Full Stack

Mihnea-Octavian Manolache es ingeniero Full Stack y DevOps en WebScrapingAPI, donde se encarga de desarrollar funciones para los productos y de mantener la infraestructura que garantiza el buen funcionamiento de la plataforma.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.