Saltar al contenido
ProxyHub

Scraping avanzado: rate limits, sesiones, reintentos y rotación sin sobrecargar los sitios

Publicado el · 6 min de lectura

Un scraper que funciona con diez páginas a menudo se atasca con diez mil: llegan los 429, los captchas, las páginas vacías que responden 200. Casi siempre el problema no es la IP, sino la forma en que las peticiones llegan al sitio. Este artículo retoma las bases de [Web scraping con proxies](/blog/web-scraping-con-proxies) y va un paso más allá: cómo montar una recogida de datos que aguante en el tiempo y siga siendo ligera para los sitios que visita.

Leer las señales del sitio

Un sitio comunica sus límites de formas distintas, y un scraper robusto las reconoce todas:

  • 429 Too Many Requests: has superado el ritmo aceptado. A menudo llega con la cabecera Retry-After, que indica cuánto esperar: un número de segundos o una fecha. Ver también error 429.
  • 503 Service Unavailable: el servidor está sobrecargado o en mantenimiento. También aquí puede haber Retry-After.
  • 403 Forbidden: el acceso está denegado. Repetir la misma petición no suele servir: revisa cabeceras, sesión y ritmo.
  • Cabeceras de cuota: algunas API y algunos sitios indican las peticiones restantes con cabeceras como X-RateLimit-Remaining. Si están, úsalas para frenar antes del límite.
  • Bloqueos silenciosos: la respuesta es 200, pero contiene un captcha, una página de consentimiento o una lista vacía. Solo se detectan revisando el contenido.
Valida siempre el contenido, no solo el código de estado: busca el elemento que esperas (el precio, el título, la tabla) y trata como error la página que no lo contiene.

Concurrencia por dominio, no peticiones por segundo

La carga que nota un sitio depende de cuántas peticiones tienes abiertas hacia él al mismo tiempo, no del total que haces en una hora. Por eso el primer parámetro que hay que ajustar es la concurrencia por dominio: cuántas peticiones paralelas van al mismo sitio.

  • Empieza bajo: una o dos peticiones paralelas por dominio, con pausas variables entre ellas.
  • Adapta el ritmo a los tiempos de respuesta: si las páginas empiezan a responder más despacio, el servidor está bajo carga y conviene frenar.
  • Respeta robots.txt: las reglas de acceso son estándar (RFC 9309). La directiva Crawl-delay no forma parte del estándar y Google la ignora, pero si un sitio la indica es una señal clara del ritmo que acepta de buen grado.
  • Reparte en el tiempo: mejor una ronda lenta y constante durante la noche que una ráfaga en la hora punta del sitio.

En Scrapy este comportamiento se consigue con la extensión AutoThrottle (AUTOTHROTTLE_ENABLED), que adapta el retardo a la latencia del sitio, junto con CONCURRENT_REQUESTS_PER_DOMAIN, DOWNLOAD_DELAY y ROBOTSTXT_OBEY.

Sesiones: cuándo mantener la misma IP

Para un sitio, una sesión es un conjunto coherente: la misma IP, las mismas cookies, el mismo user agent y el mismo idioma. Si uno de ellos cambia a mitad, la sesión parece haber pasado a otra persona.

  • Mantén la misma IP cuando el sitio usa login, carrito, filtros guardados o paginación con token: es una sesión sticky.
  • Cambia de IP entre una sesión y otra, no durante: cierra la sesión, rota la IP y vuelve a empezar con cookies nuevas.
  • Un proxy por flujo: si recoges datos de varios sitios en paralelo, da a cada flujo su propio proxy, así una rotación no interrumpe a los demás.

El razonamiento completo sobre cuándo rotar y cuándo no está en Rotación de IP.

Reintentos bien hechos: backoff, jitter y un tope

  1. Reintenta solo lo que tiene sentido: timeouts, errores de red, 429, 502, 503 y 504. Un 404 o un 403 seguirán igual.
  2. Respeta Retry-After cuando esté: es el sitio el que te dice cuánto esperar.
  3. Si no, backoff exponencial con jitter: esperas que crecen (5, 10, 20, 40 segundos) con una parte aleatoria, para que varios procesos no reintenten todos en el mismo instante.
  4. Pon un tope: un número máximo de intentos y una espera máxima. A partir de ahí, guarda la URL y sigue adelante.
  5. Pausa por dominio: si los errores en un sitio superan un umbral (por ejemplo, el 10 % de las últimas cien peticiones), detén todo el trabajo en ese sitio durante unos minutos en lugar de insistir.
Con un proxy móvil, durante un cambio de IP la conexión se corta unos segundos: una petición interrumpida en ese momento simplemente se repite, no cuenta como error del sitio.

Rotación con criterio

Con ProxyHub cambias de IP con el enlace de rotación, la API o la rotación automática. El enlace y la API responden cuando el cambio está completo, con la nueva IP, y un proxy puede cambiar de IP como máximo una vez cada 60 segundos.

  • Rota entre una ronda y otra, no en cada petición: cada sesión nueva parte de una IP nueva.
  • Para volúmenes altos usa varios proxies en paralelo: cada uno tiene su IP dedicada y, mientras uno cambia, los demás siguen.
  • Tras un límite, frena antes de rotar: si un sitio te limita, espera lo que indica y vuelve a empezar más despacio. Cambiar de IP y seguir al mismo ritmo lleva al mismo resultado.

Menos carga para el sitio, menos trabajo para ti

  • Peticiones condicionales: guarda ETag y Last-Modified y envía If-None-Match o If-Modified-Since. Si la página no ha cambiado, el servidor responde 304, sin cuerpo.
  • Sitemap: para descubrir las páginas usa el sitemap del sitio en lugar de seguir todos los enlaces.
  • Compresión: acepta gzip o br en la cabecera Accept-Encoding.
  • Solo lo necesario: en los navegadores automatizados bloquea imágenes, fuentes y vídeos. A menudo los datos llegan de una llamada JSON que puedes leer directamente.
  • Recogida incremental: vuelve a descargar solo las páginas nuevas o modificadas, no todo el sitio en cada ronda.

Con Playwright, bloquear los recursos pesados requiere una línea:

Python · Playwright
page.route("**/*.{png,jpg,jpeg,gif,webp,woff,woff2,mp4}", lambda route: route.abort())

La configuración del proxy en Playwright se explica en la guía Playwright.

Ejemplo en Python: Retry-After, backoff y rotación

Un esqueleto que respeta Retry-After, usa backoff con jitter y cambia de IP solo tras un límite repetido, teniendo en cuenta los 60 segundos entre un cambio y otro:

Python
import random, time, requests
from email.utils import parsedate_to_datetime

PROXY = "http://USUARIO:CONTRASEÑA@HOST:PUERTO"
ROTATE = "ENLACE_DE_ROTACION"
EXPECTED = 'class="price"'                # elemento que la página debe contener
session = requests.Session()
session.proxies = {"http": PROXY, "https": PROXY}
last_rotation = 0.0

def retry_after(r):
    v = r.headers.get("Retry-After", "")
    if v.isdigit():
        return int(v)
    try:
        return max(0, parsedate_to_datetime(v).timestamp() - time.time())
    except (TypeError, ValueError):
        return None

def rotate():
    global last_rotation
    if time.time() - last_rotation >= 60:     # un cambio por minuto por proxy
        requests.get(ROTATE, timeout=30)        # responde con la nueva IP
        last_rotation = time.time()
        session.cookies.clear()                 # nueva sesión, cookies nuevas

def fetch(url, tries=5):
    for attempt in range(tries):
        try:
            r = session.get(url, timeout=30)
        except (requests.ConnectionError, requests.Timeout):
            time.sleep(min(60, 2 ** attempt) + random.random())
            continue
        if r.status_code == 200 and EXPECTED in r.text:
            return r.text
        if r.status_code == 200:                  # bloqueo silencioso (captcha, página vacía)
            wait = min(120, 10 * 2 ** attempt)
        elif r.status_code in (429, 502, 503, 504):
            wait = retry_after(r) or min(120, 5 * 2 ** attempt)
        else:
            r.raise_for_status()                  # 403, 404…: no sirve reintentar
            return r.text
        if attempt >= 1:
            rotate()                              # límite repetido: nueva sesión
        time.sleep(wait + random.uniform(0, 3))   # jitter
    raise RuntimeError(f"Demasiados intentos: {url}")

El mismo esquema funciona en Node.js y en las herramientas no-code: cambian la librería y la sintaxis, no las reglas. Si tu trabajo funciona de forma automática, encontrarás más consejos en la página Proxies para automatizaciones y bots.

Preguntas frecuentes

¿Cuántas peticiones por segundo puedo hacer?

No hay un número válido para todos los sitios. Empieza con una o dos peticiones paralelas por dominio y pausas de unos segundos, y aumenta solo mientras los errores y los tiempos de respuesta se mantengan estables. Si el sitio indica Retry-After o Crawl-delay, sigue esos valores.

¿Tengo que cambiar de IP en cada petición?

No: cambiar de IP a mitad de sesión rompe logins, carritos y paginación, y no reduce la carga sobre el sitio. Cambia de IP entre una sesión y otra; con ProxyHub cada proxy puede hacerlo una vez cada 60 segundos, y para volúmenes altos conviene usar varios proxies en paralelo.

¿Cómo sé si el sitio me limita aunque responda 200?

Revisa el contenido: si falta el elemento que buscas, aparece un captcha o la página es mucho más corta de lo normal, trátala como un error. Registra estos casos aparte: son la primera señal de que el ritmo es demasiado alto.

Prueba un proxy móvil 5G

Smartphone dedicado, tráfico ilimitado, IP reales de operadores. Activo en pocos segundos.

Ver planes

O elige el país:

Sigue leyendo

Todos los artículos