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.
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
- Reintenta solo lo que tiene sentido: timeouts, errores de red, 429, 502, 503 y 504. Un 404 o un 403 seguirán igual.
- Respeta Retry-After cuando esté: es el sitio el que te dice cuánto esperar.
- 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.
- 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.
- 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.
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:
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:
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 planesO elige el país:
