Scraping de un sitio web dinámico

Los sitios web dinámicos cargan contenido usando JavaScript después de la carga inicial de la página. Esta guía muestra cómo recopilar datos de estos sitios usando el endpoint de navegador de FourA.

El problema

Cuando envías una solicitud HTTP estándar a un sitio web con mucho JavaScript, obtienes la estructura HTML pero no el contenido real. Los datos que necesitas (listados de productos, precios, resultados de búsqueda) son cargados por JavaScript después de que la página se renderiza en un navegador.

Esto es cada vez más común con frameworks modernos como React, Vue, Angular y Next.js.

La solución: Solicitudes de navegador

El endpoint de navegador de FourA (POST /api/browser/) abre tu URL en una instancia del navegador Chrome que:

  1. Carga la página
  2. Ejecuta todo el JavaScript
  3. Espera a que se renderice el contenido
  4. Devuelve el HTML completamente renderizado

Paso 1: Identifica lo que necesitas

Antes de realizar la solicitud, visita la página de destino en tu navegador y usa DevTools (F12) para encontrar un fragmento de texto o elemento que confirme que el contenido se ha cargado. Por ejemplo:

  • Un nombre de producto que aparece después de que JS se renderiza
  • Una clase CSS como product-grid en el HTML renderizado
  • Una cadena de texto como "results" que solo aparece cuando se cargan los datos

Paso 2: Envía una solicitud de navegador

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/products",
    "timeout_ms": 15000,
    "checkText": "product-grid"
  }'

La opción checkText le indica a FourA que verifique que la cadena "product-grid" aparezca en la página renderizada. Si no aparece antes del tiempo de espera, la solicitud falla, lo que te permite saber que el contenido no se cargó.

Paso 3: Analizar el HTML

La respuesta contiene el HTML completamente renderizado en el campo body. Analízalo con tu biblioteca preferida:

Python (BeautifulSoup)

import requests
from bs4 import BeautifulSoup

resp = requests.post("https://eu.api.foura.ai/api/browser/", headers={
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
}, json={
    "url": "https://example.com/products",
    "timeout_ms": 15000,
    "checkText": "product-grid"
})

html = resp.json()["body"]
soup = BeautifulSoup(html, "html.parser")

for product in soup.select(".product-card"):
    name = product.select_one(".product-name").text.strip()
    price = product.select_one(".product-price").text.strip()
    print(f"{name}: {price}")

Node.js (cheerio)

import * as cheerio from 'cheerio';

const resp = await fetch('https://eu.api.foura.ai/api/browser/', {
  method: 'POST',
  headers: { 'X-API-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://example.com/products',
    timeout_ms: 15000,
    checkText: 'product-grid'
  })
});

const { body: html } = await resp.json();
const $ = cheerio.load(html);

$('.product-card').each((i, el) => {
  console.log($(el).find('.product-name').text(), $(el).find('.product-price').text());
});

Solución de problemas

¿Sigues obteniendo contenido vacío?

  • Verifica que la página realmente use renderizado de JavaScript (comprueba con "Ver código fuente" frente a DevTools)
  • Aumenta timeout_ms: algunas páginas cargan lentamente
  • Comprueba si la página requiere autenticación o cookies (usa el parámetro cookies)

¿Obtienes una página de CAPTCHA?

  • Para requests individuales/HTTP, cambia al endpoint del proxy (POST /api/proxy/) para la rotación automática de IP.
  • Para enrutar un request de navegador a través de un proxy, pasa el parámetro proxy del endpoint del navegador. El endpoint del proxy solo envuelve requests individuales/HTTP, no requests de navegador.

El parámetro proxy toma el ID de proxy opaco que devolvió una response anterior, nunca una dirección de proxy propia. Ejecuta primero una llamada POST /api/proxy/ (o POST /api/auto/), lee el campo proxy que devuelve y luego fíjalo:

# Step 1: let FourA find a working exit. The response carries "proxy": "A1B2C3".
curl -X POST "https://eu.api.foura.ai/api/proxy/" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"maxTries": 5, "request": {"method": "GET", "url": "https://example.com/products"}}'

# Step 2: render through that same exit.
curl -X POST "https://eu.api.foura.ai/api/browser/" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/products", "proxy": "A1B2C3", "timeout_ms": 20000}'

Enviar una dirección en lugar de un ID devuelve 400 Invalid proxy format. El patrón completo está en Reutilizar un proxy entre requests.

Siguientes pasos

Actualizado: 12 de agosto de 2026