Scraping de un sitio web dinámico

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

El problema

Cuando envías una request HTTP estándar a un sitio web con alto uso de JavaScript, obtienes el shell HTML pero no el contenido real. Los datos que necesitas (listados de productos, precios, resultados de búsqueda) los carga 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: Browser Requests

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

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

Paso 1: Identifica lo que necesitas

Antes de hacer la request, 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:

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

Paso 2: Envía una Browser Request

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 "product-grid" esté en la página una vez que haya terminado de cargar. Si no está, el request devuelve un error con checkText:product-grid not found, y un request fallido no se factura. checkText no hace que FourA espere más tiempo por el texto.

Paso 3: Parsear el HTML

La response contiene el HTML totalmente 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 si la página realmente utiliza renderizado con JavaScript (compara "Ver código fuente" con DevTools)
  • Aumenta timeout_ms: algunas páginas cargan lentamente
  • Comprueba si la página requiere autenticación o cookies (utiliza el parámetro cookies)

¿Aparece una página de verificación?

  • Para requests individuales/HTTP, cambia al endpoint de proxy (POST /api/proxy/) para 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 de navegador. El endpoint de proxy solo envuelve requests individuales/HTTP, no requests de navegador.

El parámetro proxy toma el proxy ID opaco que devolvió una response anterior, nunca una dirección de proxy propia. Ejecuta primero una llamada a 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 en varias requests.

Siguientes pasos

Actualizado: 27 de septiembre de 2026