Cómo scrapear sitios web renderizados con JavaScript
Muchos sitios modernos envían un documento HTML casi vacío y construyen el contenido en el navegador con JavaScript. Si tu scraper descarga la página y no encuentra productos, precios ni texto de artículos, el contenido está ahí, pero llega más tarde. Esta guía muestra cómo distinguir qué tipo de página tienes, cómo encontrar una vía más rápida que un navegador cuando existe, y cómo scrapear páginas renderizadas con Playwright en Python. Termina con una nota sobre qué hacer cuando una página renderizada muestra un captcha.
Paso 1: comprueba si el contenido está en el HTML
Abre la página con view-source: en tu navegador, o descárgala con una petición HTTP simple, y busca en el código fuente un fragmento de texto que veas en pantalla. Son habituales tres resultados:
- El texto está ahí. La página se renderiza en el servidor. Un cliente HTTP simple y un analizador de HTML bastan, y debes usarlos, porque son más rápidos y sencillos.
- El texto no está, pero hay un bloque JSON. Muchos sitios incrustan su estado en una etiqueta script, como
__NEXT_DATA__. Analiza ese JSON directamente. - El texto no está y no hay ningún bloque. La página carga su contenido con peticiones adicionales. Mira la pestaña Network de DevTools, filtrada por Fetch/XHR, mientras la página carga.
Paso 2: llama al endpoint JSON si existe
Cuando una página obtiene sus datos de un endpoint, llamar a ese endpoint directamente suele ser la mejor opción. Es más pequeño, más rápido y más estable que la página renderizada. Copia la URL de la petición, comprueba qué cabeceras exige el sitio e intenta llamarla desde un script. Hazlo solo cuando los términos del sitio lo permitan, y respeta cualquier límite de velocidad o autenticación que el sitio espere. Un endpoint no documentado puede cambiar sin previo aviso, así que escribe tu analizador para que falle de forma ruidosa cuando cambie la forma de la respuesta.
Paso 3: renderiza la página cuando sea necesario
Si el contenido necesita de verdad el navegador, controla un navegador headless. Playwright es una buena opción para Python. El ejemplo siguiente abre un listado de productos, espera a que aparezca el contenido y lee el texto de cada tarjeta:
from playwright.sync_api import sync_playwright
URL = "https://example.com/products" # una página que tengas permiso para scrapear
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(URL, wait_until="domcontentloaded", timeout=60_000)
# Espera al elemento que demuestra que los datos han llegado.
page.wait_for_selector(".product-card", timeout=30_000)
titles = page.locator(".product-card h2").all_inner_texts()
browser.close()
for title in titles:
print(title)
Esperar a un selector concreto es mucho más fiable que una pausa sleep fija. Una pausa fija es demasiado corta en un día lento o desperdicia tiempo en un día rápido. Elige el elemento que solo aparece cuando los datos ya se han cargado.
Paso 4: gestiona el scroll infinito y la paginación
Algunas páginas cargan más elementos al desplazarse. Baja hasta el final, espera a que el número de elementos crezca y para cuando deje de crecer. Las páginas con paginación numerada son más sencillas: busca el enlace a la página siguiente, síguelo y para en la última página. Establece siempre un número máximo de páginas o de desplazamientos, para que un error no mantenga tu rastreador funcionando para siempre.
def scroll_until_done(page, selector: str, max_rounds: int = 20) -> int:
previous = -1
for _ in range(max_rounds):
count = page.locator(selector).count()
if count == previous:
break
previous = count
page.mouse.wheel(0, 4000)
page.wait_for_timeout(1500) # da tiempo a que cargue el siguiente bloque
return previous
Paso 5: mantén el navegador económico
Renderizar es caro. Bloquea las imágenes, las fuentes y los archivos multimedia que no necesites, y reutiliza un mismo navegador para muchas páginas en lugar de lanzar uno nuevo cada vez. Cierra las páginas y los contextos cuando termines, para que la memoria no crezca durante una ejecución larga.
def block_heavy_assets(route):
if route.request.resource_type in ("image", "font", "media"):
route.abort()
else:
route.continue_()
page.route("**/*", block_heavy_assets)
Paso 6: cuando una página renderizada muestra un captcha
A veces la página renderizada presenta un captcha antes de mostrar cualquier contenido. Si ese captcha es PopularCaptcha, el tipo de captcha activo en Scarnox, lees la sitekey de la página, solicitas un token y colocas el token en el campo del formulario que espera el sitio. La guía de PopularCaptcha en Python recorre ese paso. Otros widgets todavía no están activos, así que consulta tipos de captcha explicados antes de planificar en torno a uno.
Ten cuidado con el propio navegador. Un navegador headless que parece claramente automatizado puede activar más comprobaciones, no menos. Lee detección de navegadores headless para ver las señales a vigilar, y prefiere menos cargas de página, hechas más despacio.
Scrapea de forma responsable
- Revisa el
robots.txty los términos del sitio antes de renderizar o llamar a sus endpoints. - Mantén los límites de velocidad bajos, y guarda los resultados en caché para pedir cada página una sola vez.
- No scrapees datos personales sin una base legal para hacerlo.
- Si un sitio restringe el acceso, para. No intentes saltarte la restricción.
Próximos pasos
- Scrapea sin que te bloqueen
- Detección de navegadores headless
- Scrapear sitios protegidos por Cloudflare
- Gestión de captchas con Playwright y Puppeteer
Empieza con crédito de prueba gratis, sin tarjeta: crea una cuenta y prueba el flujo de PopularCaptcha en una página que sea tuya. Para ver las tarifas actuales, consulta la página de precios.