Cómo hacer web scraping sin que te bloqueen

Publicado:

18 min de lectura

Acar Diveroli
Autor: Acar Diveroli
Un reloj de límites de velocidad y una tarjeta de cabeceras que alimentan un cubo scraper, seguido de un nodo de rotación y un sitio web

Un script de seguimiento de precios funciona bien el primer día, devuelve páginas medio vacías el segundo y recibe un 403 en todas las peticiones el tercero. Casi todo el que escribe scrapers pasa por esto, y la primera reacción suele ser buscar «más IP» o «esconderse mejor». La mayoría de los bloqueos tienen una causa más corriente: decenas de peticiones por segundo, cabeceras que nunca coinciden con un navegador real, toda la carga concentrada en una sola dirección IP y reglas que el sitio publica abiertamente y que nadie leyó.

En este artículo explicamos por qué se bloquean las herramientas de web scraping, los síntomas de un bloqueo y las causas que hay detrás. Después vemos cómo ajustar la velocidad de las peticiones, mantener coherentes las cabeceras, cuándo hace falta realmente rotar la IP, usar una IP fija donde se necesita sesión, y robots.txt y las condiciones del sitio. No es una guía para «esquivar la protección»: «sin que te bloqueen» significa aquí no toparse con bloqueos porque no perjudicas al sitio y sigues sus reglas.

¿Por qué los sitios bloquean los scrapers?

Detrás de que un sitio limite el tráfico automatizado suele haber costos concretos más que una suposición de mala fe:

  • Carga del servidor. Un solo script que envía cientos de peticiones por segundo puede consumir los recursos que una tienda online pequeña reserva para sus visitantes reales. En páginas de búsqueda y filtros que consultan la base de datos, el efecto se multiplica.
  • Ancho de banda y costo de infraestructura. Cada petición aparece en la factura del servidor y de la CDN del sitio.
  • Protección del contenido y de los datos comerciales. Cuando la competencia extrae en masa precios, stock y datos de anuncios, el propietario del sitio puede querer restringirlo.
  • Seguridad. Los inicios de sesión por fuerza bruta, las pruebas de tarjetas y el acaparamiento de inventario también se hacen con tráfico automatizado. A primera vista, los sistemas de protección no pueden distinguir ese tráfico de un scraper legítimo.

La mayoría de estas decisiones no se toman en el propio código del sitio, sino en la capa de gestión de bots que tiene delante. Las CDN y los servicios de seguridad puntúan cada petición con señales como la velocidad, la reputación de la IP, la coherencia de las cabeceras y el comportamiento. Para un ejemplo de cómo esa capa clasifica el tráfico de bots, consulta Cloudflare Precursor.

¿Cuáles son los síntomas de un bloqueo?

Los bloqueos no siempre llegan con un mensaje de error claro. Si ves uno de estos síntomas en los datos que produce tu scraper, la causa probable es un bloqueo:

  • 403 Forbidden: la petición se entendió pero se rechazó. Puede deberse a la reputación de la IP, a cabeceras ausentes o a una restricción geográfica.
  • 429 Too Many Requests: superaste el límite de velocidad. A menudo una cabecera Retry-After indica cuánto esperar.
  • 503 Service Unavailable: el servidor puede estar realmente ocupado, o un sistema de protección de bots puede estar devolviendo esta respuesta temporalmente.
  • 200 pero con página de verificación: el código de estado parece correcto, pero el contenido es una pantalla de verificación en lugar de una lista de productos.
  • 200 pero con contenido vacío o incompleto: tus selectores no encuentran nada. Puede deberse a un bloqueo o a que la página carga su contenido con JavaScript.
  • Redirecciones a una página de inicio de sesión: tu sesión se ha cerrado, a menudo por un cambio de IP en mitad de la sesión.
  • Respuestas cada vez más lentas: algunos sistemas retrasan la respuesta a propósito en lugar de rechazar la petición.

Explicamos cada código de estado HTTP y cuáles conviene reintentar en Códigos de estado HTTP en web scraping. Para saber si un contenido vacío es un bloqueo o una página dinámica, consulta Páginas estáticas y dinámicas.

SíntomaCausa probableSolución legítima
Muchos 429 en poco tiempoLa velocidad supera el límite del sitioBajar la concurrencia, esperar lo que indique Retry-After
403 desde la primera peticiónCabeceras ausentes, una IP de centro de datos o una restricción regionalUsar cabeceras coherentes; elegir un tipo de IP y una ubicación acordes con el público del sitio
403 que empieza al cabo de un ratoSe marcó el tráfico intenso desde una sola IPIr más despacio y repartir la carga en el tiempo y, si hace falta, entre IP
Página de verificaciónComportamiento o reputación sospechosos de la IPParar, revisar velocidad y alcance; buscar una API o un permiso
200 pero contenido vacíoLa página carga con JavaScript o el contenido se ocultóEncontrar la petición de API en segundo plano; un navegador headless si hace falta
La sesión se cierra de golpeLa IP cambió en mitad de la sesiónUsar una IP fija para la sesión
Las respuestas se ralentizan con el tiempoCarga del servidor o retraso deliberadoBajar la velocidad, evitar las horas punta
El contenido difiere del de un navegador realContenido específico para bots o diferencia de ubicaciónRevisar la ubicación; usar una identidad de cliente que te presente

Velocidad de las peticiones: ¿cómo se respetan los límites?

La causa de bloqueo más común y más fácil de evitar es la velocidad. Una persona en una tienda online abre una página cada pocos segundos; un scraper escrito con asyncio puede enviar cientos de peticiones en el mismo tiempo. RFC 6585 define el código 429 Too Many Requests exactamente para esta situación y dice que el servidor puede indicar cuánto esperar con una cabecera Retry-After.

Las reglas básicas para mantener la velocidad bajo control:

  1. Fija un límite de concurrencia por sitio. Mantén pequeño el número de peticiones abiertas al mismo dominio. La concurrencia total puede ser alta, pero la parte que recae en un solo sitio debe ser baja.
  2. Añade pausas aleatorias entre peticiones. Una petición cada 2 segundos exactos llama más la atención que intervalos irregulares y no evita picos momentáneos.
  3. Ve más despacio, no más rápido, cuando recibas 429 y 503. Reenviar de inmediato una petición fallida empeora el problema. Usa backoff exponencial.
  4. Respeta la cabecera Retry-After. El valor puede ser un número de segundos o una fecha HTTP; la explicación de MDN muestra ambas formas.
  5. No envíes peticiones innecesarias. Para no volver a descargar páginas que no han cambiado, guarda los valores ETag y Last-Modified y envía peticiones condicionales con If-None-Match e If-Modified-Since; si la página no ha cambiado, el servidor devuelve un 304 sin cuerpo.
  6. Usa el sitemap. Empieza por las direcciones del sitemap.xml del sitio en lugar de rastrear todo el sitio enlace a enlace.
  7. Evita las horas punta. No hagas recogidas masivas cuando el público del sitio está más activo.

La función de Python siguiente respeta la cabecera Retry-After en las respuestas 429, 502, 503 y 504; si no hay cabecera, aplica backoff exponencial con un componente aleatorio. No reintenta respuestas como 403 y 404, porque reenviarlas no cambia el resultado:

python
import random
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

import requests

RETRY_STATUS = {429, 502, 503, 504}


def retry_after_seconds(value):
    """Retry-After puede ser segundos o una fecha HTTP."""
    if not value:
        return None
    if value.isdigit():
        return int(value)
    try:
        when = parsedate_to_datetime(value)
    except (TypeError, ValueError):
        return None
    return max(0.0, (when - datetime.now(timezone.utc)).total_seconds())


def polite_get(session, url, max_attempts=5, base=1.0, cap=60.0):
    for attempt in range(max_attempts):
        try:
            response = session.get(url, timeout=20)
        except (requests.ConnectionError, requests.Timeout):
            response = None

        if response is not None and response.status_code not in RETRY_STATUS:
            return response  # respuestas como 200, 404 y 403 no se reintentan

        wait = None
        if response is not None:
            wait = retry_after_seconds(response.headers.get("Retry-After"))
        if wait is None:
            wait = min(cap, base * 2**attempt) * random.uniform(0.5, 1.0)
        time.sleep(min(wait, cap))

    raise RuntimeError(f"{url}: sin respuesta tras {max_attempts} intentos")

Basta con usar esta función sobre una lista de páginas con una pausa entre peticiones:

python
session = requests.Session()
session.headers.update({
    "User-Agent": "ExamplePriceBot/1.0 (+https://example.com/about-our-bot)",
    "Accept-Language": "es-ES,es;q=0.9",
})

for url in urls:
    response = polite_get(session, url)
    if response.status_code == 403:
        print("Acceso denegado, parar e investigar:", url)
        break
    process(response.text)
    time.sleep(random.uniform(2, 5))

Cómo ajustar la concurrencia y qué limita realmente la velocidad se explica en Concurrencia y paralelismo.

Cabeceras y una identidad de cliente coherente

Las cabeceras de una petición HTTP dicen quién es el cliente y qué acepta. Las cabeceras por defecto de las librerías son muy distintas de las de un navegador. Python Requests envía por defecto un User-Agent como python-requests/2.x; muchos sitios rechazan ese valor directamente.

Aquí hay dos enfoques, y sirven a fines distintos:

Presentarte. Los crawlers legítimos ponen en el User-Agent el nombre del bot y una dirección con información sobre él: ExamplePriceBot/1.0 (+https://example.com/about-our-bot). Cuando el administrador del sitio ve el tráfico, sabe quién lo envía y cómo contactarte; si hay un problema, puede escribirte en lugar de bloquearte. También puede escribir reglas de robots.txt específicas para ese nombre.

Coherencia. Uses la identidad que uses, debe ser coherente en toda la petición. Ejemplos de incoherencia:

  • Un User-Agent que cambia al azar en cada petición, pero con las mismas cookies y la misma IP.
  • Un User-Agent que dice ser Chrome, pero sin ninguna de las cabeceras Accept, Accept-Language y Sec-CH-UA que Chrome envía en cada petición.
  • Visitar un sitio de Türkiye con Accept-Language: en-US y una IP que sale en EE. UU. esperando precios en liras turcas.

Explicamos qué señales, además de las cabeceras, identifican a los navegadores en Browser fingerprinting. Falsificar estas señales para esquivar la protección de bots no es lo que recomienda este artículo; el objetivo es que tu cliente diga de forma coherente lo que es.

Diversidad de IP: ¿cuándo hace falta rotar?

El tráfico intenso desde una sola dirección IP es la unidad a la que más fácil resulta aplicar límites de velocidad. Por eso «me han bloqueado, cambio de IP» es un reflejo tan común. Pero la rotación tiene dos fines legítimos y un uso equivocado.

Fin legítimo 1: repartir la carga. Un trabajo que recoge páginas públicas de precios de cientos de sitios distintos y envía todo el tráfico desde una sola IP da a esa IP mala reputación enseguida, y puede hacer que la marquen en listas de reputación generales aunque no supere el límite de ningún sitio concreto. Repartir el tráfico entre distintas direcciones mantiene en un nivel realista la carga de cada dirección.

Fin legítimo 2: ver contenido según la ubicación. Los precios, el stock y los resultados de búsqueda cambian según el país y la ciudad del visitante. Salir desde distintas ubicaciones es la única forma de recoger la vista real de cada mercado.

Uso equivocado: esquivar un bloqueo explícito. Si el sitio te ha avisado con un límite de velocidad, ha cerrado esa ruta en robots.txt o prohíbe expresamente el acceso automatizado en sus condiciones, cambiar de IP y seguir a la misma velocidad no resuelve nada; significa ignorar una preferencia que el propietario del sitio ha expresado con claridad.

La rotación puede configurarse con un Proxies rotativos, que da una IP de salida distinta en cada conexión a través de una sola dirección. Para trabajos que necesitan direcciones de conexiones domésticas reales, se prefiere un Proxies residenciales. Mostramos un esquema rotativo en Python en Cómo rotar proxies en Python.

Una IP fija donde se necesita sesión

Cambiar de IP en cada petición no conviene para todos los trabajos. En estos, la dirección IP debe mantenerse fija durante un tiempo:

  • Páginas con sesión iniciada. Si extraes informes del panel de tu propia cuenta, un cambio de IP en mitad de la sesión activa controles de seguridad y la sesión se cierra.
  • Flujos de varios pasos. Flujos que guardan estado en el servidor, como añadir al carrito, seleccionar filtros o paginar.
  • Visitas ligadas a cookies. Peticiones con la misma cookie que llegan desde países distintos crean una imagen de visitante incoherente.

Para estos trabajos, un Proxies de sesión fija mantiene la misma IP de salida durante un periodo definido. La regla general: una sesión = una IP; la IP puede cambiar cuando termina la sesión.

robots.txt y condiciones del sitio

Antes de recoger datos de un sitio, hay que revisar dos documentos.

robots.txt es el archivo en el que un sitio indica qué rutas no quiere que rastreen los bots, y su formato está estandarizado en RFC 9309. No rastrear las rutas cerradas con Disallow significa respetar la preferencia que el sitio ha expresado explícitamente. Explicamos cómo leer el archivo y comprobarlo con Python en Qué es robots.txt y cómo leerlo.

Las condiciones de uso pueden contener cláusulas sobre el acceso automatizado. Algunos sitios prohíben por completo el scraping, otros lo limitan a cierta velocidad y otros ofrecen una API oficial para los datos. Si hay una API oficial, casi siempre es la vía más sólida: los datos llegan estructurados, tu código no se rompe cuando cambia el diseño de la página y tu acceso se basa en un acuerdo.

Las páginas con datos personales requieren un cuidado adicional; las obligaciones de la KVKK en Türkiye y del RGPD en Europa se aplican con independencia de cómo recojas los datos. Tratamos el marco legal en ¿Es legal el web scraping?.

Algunos sitios también colocan enlaces ocultos que los visitantes no ven pero en los que caen los bots. Un crawler que sigue enlaces visibles y mantiene un alcance acotado se mantiene lejos de estas trampas de forma natural; explicamos el mecanismo en Trampas honeypot.

¿Qué hacer cuando aparece un CAPTCHA?

Una pantalla de verificación ante un scraper significa que el sitio considera sospechoso tu tráfico. En este punto no se recomiendan servicios de resolución de CAPTCHA ni plugins para evadir la detección; suponen intentar esquivar un control explícito del sitio y suelen ocultar el origen del problema. En su lugar, sigue este orden:

  1. Detén el scraper. Seguir enviando peticiones a la pantalla de verificación baja aún más la reputación de la IP y de la sesión.
  2. Mide la velocidad. Comprueba cuántas peticiones enviaste al mismo sitio en el último minuto.
  3. Compara las cabeceras con un navegador real. ¿Falta alguna cabecera o hay alguna contradictoria?
  4. Revisa el alcance. ¿Estás rastreando rutas cerradas por robots.txt o páginas que no necesitas?
  5. Busca alternativas. Una API oficial, una opción de exportación de datos o el contacto directo con el propietario del sitio.
  6. Solo entonces vuelve a intentarlo a menor velocidad con una identidad de cliente coherente.

Qué hacer si te bloquean: lista de diagnóstico

  • ¿Sabes cuándo empezó el bloqueo y la velocidad de peticiones en ese momento?
  • ¿Cuál es el código de respuesta: 403, 429, 503 o 200 con una página de verificación?
  • ¿Llegó una cabecera Retry-After y la respetaste?
  • ¿La misma dirección se abre en un navegador real con la misma IP?
  • ¿Tus cabeceras son coherentes, o el User-Agent es el predeterminado de la librería?
  • ¿Las rutas que rastreas están cerradas en robots.txt?
  • ¿Qué dicen las condiciones del sitio sobre el acceso automatizado?
  • ¿Cambia la IP en un flujo que necesita sesión?
  • ¿El contenido vacío viene de una página que carga con JavaScript?
  • ¿Hay una API oficial que ofrezca los mismos datos?

Casos de uso

  • Seguimiento de precios: unas pocas veces al día, direcciones de producto tomadas del sitemap, solo las páginas que han cambiado mediante peticiones condicionales. La configuración está en nuestra página de solución de seguimiento de precios.
  • Investigación de mercado y recogida de catálogos: páginas públicas de muchos sitios distintos, concurrencia baja por sitio y rotación para repartir la carga. La configuración general está en nuestra página de solución de extracción de datos.
  • Rastreo amplio: un crawler que sigue enlaces, respeta robots.txt y mantiene una cola por dominio. La parte del escalado está en nuestra página de solución de web crawler.
  • Seguimiento de resultados de búsqueda por ubicación: primero la API oficial y después puntos de salida por ciudad. Los detalles están en Cómo automatizar el seguimiento de posiciones SEO.
  • Selectores robustos: aunque no te bloqueen, los datos llegan vacíos cuando cambia la estructura de la página. Explicamos cómo escribir selectores que no se rompan en Selector CSS o XPath.

Errores comunes

  • Iniciar cientos de peticiones a la vez con Promise.all o asyncio.gather. La velocidad total parece alta, pero la carga en cada sitio se vuelve inaceptable.
  • Reintentar de inmediato ante un 429. Los reintentos sin backoff prolongan el límite de velocidad.
  • Usar el User-Agent predeterminado de la librería. Muchos sitios bloquean ese valor directamente.
  • Generar un User-Agent aleatorio en cada petición. Una identidad cambiante con la misma IP y las mismas cookies es una señal de incoherencia.
  • Cambiar de IP en cada petición en un trabajo que necesita sesión. La sesión se cierra y te redirigen a la página de inicio de sesión.
  • Confundir un código de estado correcto con datos correctos. Una página de verificación devuelta con 200 produce datos rotos en silencio porque los selectores vuelven vacíos. Comprueba que existe un elemento esperado en el contenido de la respuesta.
  • No leer robots.txt. Rastrear sin conocer la preferencia del sitio es un mal comienzo tanto ético como legal.

Guía de decisión

Tu situaciónRecomendación
Hay una API oficialPrimero la API
Pocas páginas de un solo sitioUna sola IP, velocidad baja, cabeceras coherentes
Páginas públicas de muchos sitiosConcurrencia baja por sitio + proxy rotativo
Contenido de otro país o ciudadProxy residencial con selección de ubicación
Tu propia cuenta con sesión iniciadaIP de sesión fija o estática, la misma dirección durante la sesión
Recibes 429Espera lo que indique Retry-After, baja la concurrencia
Aparece una pantalla de verificaciónPara, sigue la lista de diagnóstico, busca alternativas
La página vuelve vacíaComprueba primero si carga de forma dinámica
La ruta está cerrada en robots.txtNo la rastrees

Preguntas frecuentes

¿Usar un proxy evita los bloqueos por completo?

No. Un proxy reparte el tráfico entre distintas direcciones IP y permite ver contenido según la ubicación, pero las peticiones que se envían demasiado rápido, llevan cabeceras incoherentes o van a rutas que el sitio ha cerrado se bloquean vengan de la IP que vengan. Un proxy funciona junto con la velocidad adecuada y un cliente coherente.

¿Cuántas peticiones por segundo son seguras?

No hay un valor seguro único para todos los sitios; depende de la infraestructura del sitio, del peso de la página y de la hora. Si robots.txt indica un Crawl-delay, respétalo. Si no, empieza a baja velocidad, sube poco a poco vigilando los 429 y los tiempos de respuesta, y retrocede en cuanto veas que el servidor se ralentiza.

¿El User-Agent debe ser un valor de navegador o un nombre de bot?

Para un crawler legítimo, lo recomendable es poner el nombre de tu bot y una dirección de contacto. El administrador del sitio ve quién eres y puede contactarte si hay un problema. Algunos sitios bloquean por defecto a los bots desconocidos; en ese caso, pedir permiso o buscar una API oficial es una vía más sólida que intentar parecer un navegador.

¿Usar un navegador headless reduce el riesgo de bloqueo?

Te permite ver datos en páginas que cargan con JavaScript, pero por sí solo no reduce el riesgo de bloqueo. Al contrario, cada página genera decenas de peticiones adicionales (imágenes, scripts, hojas de estilo) y carga más el sitio. Encontrar la petición de API que la página hace en segundo plano suele ser más eficiente.

Han bloqueado mi IP. ¿Cuánto durará?

Depende del sitio: desde un límite de velocidad de unos minutos hasta una lista negra de días. Si hay una cabecera Retry-After, indica la duración. Si no, espera un tiempo y envía una sola petición a muy baja velocidad para comprobarlo; no sigas a la misma velocidad antes de que se levante el bloqueo.

¿Necesito todas estas precauciones para un trabajo pequeño y puntual?

Para un trabajo puntual de unas decenas de páginas, poner unos segundos entre peticiones, usar un User-Agent con sentido y revisar robots.txt suele bastar. Las demás precauciones cobran importancia cuando el trabajo se vuelve regular y a gran escala.

En resumen

La mayoría de los scrapers no se bloquean por no saber esconderse, sino porque saturan el sitio y parecen incoherentes. Limita la velocidad de las peticiones por sitio, respeta las respuestas 429 y Retry-After, no vuelvas a descargar páginas que no han cambiado, usa una identidad de cliente coherente que te presente y lee robots.txt y las condiciones del sitio. La rotación de IP sirve para repartir la carga y ver contenido según la ubicación, y una IP de sesión fija, para trabajos que necesitan sesión; ninguna de las dos es una herramienta para esquivar un bloqueo explícito. Encontrarás tipos de proxy adecuados para tu trabajo de recogida de datos en nuestros servicios de proxy.