Scraping

Cómo construir un Target Health Score: saber cuándo un sitio se está volviendo en tu contra

Los sitios rara vez bloquean un rastreador en un solo paso. Primero desafían, aplican bloqueos parciales y ralentizan. Cómo convertir esas señales en una puntuación por sitio, y actuar en consecuencia.

Matt Brown

Matt Brown

22 de septiembre de 2026 · 9 min de lectura

Un sitio casi nunca pasa de servir a tu crawler con normalidad a bloquearlo por completo en un solo paso. Lo que ocurre primero es más discreto. Unas cuantas solicitudes más llegan a una página de desafío. Algunas respuestas vuelven con 200 OK sin nada útil dentro. La latencia va subiendo poco a poco. Un puñado de registros no pasa la validación. Cada señal por separado parece ruido, y cada una vive en un panel distinto, así que nadie las conecta hasta que el conjunto de datos tiene un agujero.

Una puntuación de salud del objetivo (target health score) es la solución: un número por sitio que indica si ese sitio se sigue comportando como es habitual en él, con los motivos adjuntos. Este artículo cubre qué señales alimentan esa puntuación, cómo combinarlas sin engañarte a ti mismo, y qué debe hacer el crawler cuando el número cae.

La salud del objetivo no es la salud del proxy

Merece la pena precisar qué se está midiendo, porque ambas cosas se confunden.

La salud del proxy pregunta si una ruta funciona: una puerta de enlace, un país, una sesión, una salida. La salud del objetivo pregunta si un sitio concreto sigue dispuesto y capaz de servirte. Una ruta de proxy muerta falla contra todos los sitios. Un sitio que se vuelve en tu contra falla en todas las rutas.

Esa diferencia es el primer diagnóstico. Cuando suben los fallos, sepáralos por ruta. Si se concentran en un país, un grupo o un patrón de sesión, es un problema de ruta, y el enfoque de monitoring residential proxy health at scale es aplicable. Si suben de forma uniforme en todas las rutas que envías a ese sitio, el sitio ha cambiado de actitud hacia ti, y para eso sirve esta puntuación.

Las señales

Agrupa las señales según lo que revelan. Algunas son evidentes, otras casi silenciosas, y las silenciosas son las más caras de pasar por alto.

SeñalQué aspecto tienePor qué importa
Bloqueos duros403, 429, reinicios de conexiónRechazo explícito; fácil de contar
DesafíosCAPTCHA o páginas intersticialesEl sitio sospecha automatización y te está probando
Bloqueos suaves200 OK con una página de bloqueo, resultados vacíos o un diseño recortadoRechazo disfrazado de éxito
Deriva de latenciaEl tiempo de respuesta p95 sube respecto a lo habitual en ese sitioA menudo una ralentización deliberada, a veces solo carga
Fallos de extracciónFaltan campos obligatorios, el esquema ya no coincideLa página ha cambiado, o te están sirviendo algo distinto
Deriva de costeMás reintentos, bytes o créditos por registro válidoTodo lo anterior, expresado en dinero

Los bloqueos suaves merecen atención especial porque los códigos de estado mienten. En una medición de 2023 sobre el geobloqueo desde Cuba, 32 dominios servían sus páginas de bloqueo con un estado 200 OK, como se describe en which countries get geo-blocked most. Un crawler que confía en el código de estado registra eso como éxitos. Detecta los bloqueos suaves por el contenido: marcadores conocidos de página de bloqueo, un tamaño de respuesta muy por debajo de lo normal para ese tipo de página, o una extracción que no devuelve nada donde siempre devolvió algo.

Dos responsables, dos puntuaciones

Una distinción evita muchísima confusión: separar “el sitio ha cambiado” de “el sitio se ha vuelto en tu contra”.

Un rediseño rompe tu parser. Todas las páginas cargan bien, pero los campos obligatorios desaparecen. Eso es un problema de extracción con una solución de código, a cargo de quien mantiene el parser. Un sitio que empieza a desafiar y a aplicar bloqueos suaves es un problema de relación, y la solución es un cambio en cómo, cuánto o si recopilas. Responsables distintos, respuestas distintas.

Así que calcula la hostilidad, es decir, bloqueos, desafíos, bloqueos suaves y latencia, como la puntuación de salud, y haz un seguimiento de la validez de la extracción por separado, como una señal de salud del parser distinta. Cuando ambas caen a la vez, mira primero la hostilidad: un sitio que sirve páginas de desafío también romperá cualquier extractor.

Puntúa contra lo habitual en ese sitio

El error más común es usar umbrales globales. Una tasa de desafío del 5% es alarmante en un sitio que nunca te ha desafiado, y totalmente normal en uno que desafía a todo el mundo en la primera visita. Cada señal hay que compararla con lo habitual en ese sitio, en una ventana lo bastante larga como para ser estable, por ejemplo las dos semanas anteriores, y excluyendo el día más reciente, para que un mal día no se convierta en el nuevo normal.

Después, pondera, limita y suma:

from dataclasses import dataclass


@dataclass
class Window:
    requests: int
    hard_blocks: int      # 403, 429, connection resets
    challenges: int       # CAPTCHA or interstitial pages
    soft_blocks: int      # 200 OK carrying a block page or an empty result
    p95_latency_ms: float


MIN_REQUESTS = 50
WEIGHTS = {"hard_block": 35, "challenge": 25, "soft_block": 25, "latency": 15}


def signals(w):
    n = max(1, w.requests)
    return {
        "hard_block": w.hard_blocks / n,
        "challenge": w.challenges / n,
        "soft_block": w.soft_blocks / n,
        "latency": w.p95_latency_ms,
    }


def badness(name, now, base):
    if name == "latency":
        # Full penalty at three times the site's own normal latency.
        return min(1.0, max(0.0, (now / max(base, 1.0) - 1) / 2))
    # Full penalty at 20 percentage points above the site's own normal rate.
    return min(1.0, max(0.0, (now - base) / 0.20))


def health_score(current, baseline):
    if current.requests < MIN_REQUESTS:
        return None, ["not enough requests to judge"]
    now, base = signals(current), signals(baseline)
    penalty = {k: w * badness(k, now[k], base[k]) for k, w in WEIGHTS.items()}
    score = round(100 - sum(penalty.values()))
    reasons = [k for k, p in sorted(penalty.items(), key=lambda kv: -kv[1]) if p >= 1]
    return score, reasons

Aquí hay varias decisiones de diseño deliberadas.

  • Solo cuenta el exceso sobre la línea base. Un sitio que siempre ha desafiado el 3% de las solicitudes no pierde puntos por hacerlo hoy.
  • Cada señal está limitada. Una señal desbocada no puede hundir la puntuación por debajo de cero ni ahogar a las demás, y la lista de motivos muestra cuál ha dominado.
  • Una ventana pequeña no devuelve puntuación. Cinco solicitudes con un bloqueo no es una tasa de bloqueo del 20%, son datos insuficientes. Una puntuación ausente es más honesta que una incorrecta con confianza.
  • Los pesos son opiniones. Empieza con algo parecido a esto, y ajústalo después de revisar unos cuantos incidentes reales. Los bloqueos duros y los suaves merecen el mayor peso porque significan datos que no obtuviste.

Suaviza también la puntuación a lo largo del tiempo, por ejemplo con una media ponderada exponencialmente, para que un minuto malo no dispare una alerta, mientras que una caída sostenida se siga notando dentro de la hora.

Canarios: una verdad de referencia que controlas tú

Todas las señales anteriores son inferidas. Los canarios te dan algo más cercano a la verdad. Elige un puñado de páginas estables por sitio en las que sepas cómo debe ser la respuesta correcta: un producto cuyo precio puedes comprobar, un listado cuyo número de artículos conoces, una página cuya estructura no ha cambiado en meses. Recupéralas periódicamente, por la misma ruta que el tráfico de producción.

Cuando un canario devuelve una página que parece normal pero contiene el valor equivocado, has encontrado el fallo más difícil de detectar: contenido que se procesa perfectamente y que simplemente no es lo que vería un visitante real. Ningún código de estado ni gráfico de latencia te va a mostrar eso. Los canarios también hacen fiable la línea base, porque conoces su comportamiento correcto de forma independiente al crawler.

Vincula acciones a franjas, y mantén a las personas en el bucle

Una puntuación que nadie usa es un panel de control. Dale franjas, y da a cada franja una acción que el sistema tome por sí solo:

FranjaSignificadoAcción automática
80 a 100Se comporta como es habitualNinguna
50 a 79DegradándoseReducir la concurrencia para este sitio, alargar los intervalos de revisita, comprobar si los fallos son de todo el sitio o específicos de una ruta
Por debajo de 50El sitio se está resistiendo claramentePausar el sitio, mantener solo los canarios en marcha, avisar a una persona

La franja de degradación se conecta directamente con el resto del crawler. La menor concurrencia es para lo que sirve el limitador por host de backpressure and flow control, y una puntuación en caída debería elevar el coste efectivo de ese sitio en un cost-aware scheduler, de modo que el presupuesto se mueva hacia sitios donde todavía compra datos.

La franja inferior no está automatizada deliberadamente más allá de la pausa. Un sitio que se resiste con fuerza te está diciendo algo, y la respuesta correcta es una decisión humana: ralentizar más, comprobar si tu recopilación sigue ajustándose a los términos del sitio, buscar una API oficial o un feed de datos, o parar. Escalar automáticamente a métodos de recopilación más agresivos cada vez que la puntuación baja solo convierte una señal en una carrera armamentística, y es precisamente lo que una puntuación de salud debería ayudarte a evitar. Rate limiting and request throttling explica cómo interpretar lo que un sitio te está pidiendo.

Revísala como cualquier otra alerta

Trata la puntuación como una alerta con una tasa de falsos positivos, y revísala. Después de cada incidente, pregúntate si la puntuación se movió con suficiente antelación, si los motivos señalaron la causa real, y si la acción automática ayudó. La mayor parte del ajuste viene de dos o tres incidentes reales, no de diseñar los pesos de antemano. Guarda el historial: la puntuación a lo largo de los meses es el mejor registro que tienes de cómo cambia la actitud de cada sitio hacia el tráfico automatizado.

En resumen

Los sitios se vuelven en contra de los crawlers de forma gradual y silenciosa, mediante desafíos, rechazos disfrazados y respuestas más lentas, mucho antes de un bloqueo total. Cada señal por separado es ambigua. Comparadas con lo habitual en ese sitio, ponderadas, limitadas y combinadas, dan un único número que se mueve pronto, con los motivos adjuntos.

La puntuación resulta más valiosa por lo que permite hacer al crawler con calma: aflojar antes de ser bloqueado, mover el presupuesto a otro sitio, y dejar las decisiones difíciles en manos de una persona con las pruebas delante. Las métricas más amplias que la rodean están en monitoring a web scraping pipeline.

¿Listo para empezar?

Prueba los proxies residenciales de Shifter, más de 205M IPs, más de 195 países, desde 0,75 $/GB.

Comenzar