Si el parser de Wildberries o Ozon de repente comenzó a devolver páginas vacías, tiempos de espera y errores 403, el problema no siempre está en la plataforma misma. Más a menudo, parte de las direcciones IP en el pool de proxies ya están "caducadas": han sido bloqueadas, ralentizadas o están bajo CAPTCHA. Analizamos qué métricas diferencian un proxy funcional de uno muerto y cómo automatizar el filtrado sin control manual.
Por qué el pool de proxies se degrada con el tiempo
Cualquier pool de proxies — incluso comprado a un proveedor confiable — pierde parte de las direcciones funcionales con el tiempo. Las razones son diversas: los sistemas antifraude de Wildberries, Ozon y Avito acumulan marcas de reputación en las IP, algunas direcciones caen en listas negras después de solicitudes masivas de otros clientes del proveedor (si el proxy es compartido), los rangos de centros de datos son bloqueados en lotes por subredes, y las IP residenciales a veces simplemente se retiran del pool porque el usuario real desconectó el dispositivo.
Si no se limpia el pool, la proporción de proxies "muertos" crece de manera no lineal: primero 5%, después de una semana de scraping activo — 20-30%. El parser comienza a gastar recursos en reintentos a través de IPs no funcionales, aumenta el tiempo promedio de recolección de una página, y en los logs se acumulan errores que ocultan problemas reales (por ejemplo, cambios en el diseño del sitio). Sin un sistema de filtrado, o bien mantienes el pool inflado "por si acaso", o recibes regularmente fallos en la recolección de datos.
La tarea no es solo registrar el hecho de un error, sino clasificarlo: sobrecarga temporal del servidor, bloqueo de una IP específica, bloqueo regional o problema del lado del parser (encabezados incorrectos, cookies desactualizadas). Dependiendo del tipo de error, se determina si se debe eliminar el proxy del pool para siempre, ponerlo en "cuarentena" por un tiempo o simplemente repetir la solicitud.
Métricas clave de errores para el filtrado
Para diferenciar una falla temporal de un proxy realmente muerto, es necesario recopilar varias métricas por cada IP, y no basarse en un único estado HTTP. Aquí está el conjunto básico que se debe registrar en cada solicitud:
| Métrica | Qué indica | Acción |
|---|---|---|
| HTTP 403 / 429 | Bloqueo o límite de tasa en la IP | Cuarentena de 30-60 minutos, si se repite — eliminación |
| Timeout de conexión | Proxy sobrecargado o muerto | 3 timeouts consecutivos — eliminación del pool |
| CAPTCHA en la respuesta | IP bajo sospecha de anti-bots | Reducir la frecuencia de solicitudes o cambiar IP |
| Tiempo de respuesta > 5 seg | Degradación de la velocidad del proxy | Bajar prioridad en el pool |
| Tamaño de respuesta por debajo del promedio | Se devolvió una página de bloqueo | Verificación adicional del contenido antes de aceptar |
| Tasa de solicitudes exitosas (success rate) | Evaluación integral de la calidad de la IP | Por debajo del 70% en 50 solicitudes — eliminación |
Es importante no confundir los códigos de error del sitio con errores de red. Por ejemplo, 404 no es un problema del proxy, sino un producto ausente en Wildberries. Pero si la misma IP recibe regularmente 403 donde otras IPs reciben 200, es una señal clara de un bloqueo específico de esa dirección.
Algoritmo de scoring de proxies
En lugar de un binario "funcional / no funcional", es más conveniente calcular para cada IP un score de 0 a 100, que disminuye gradualmente con los errores y se recupera con las solicitudes exitosas. Esto permite no eliminar el proxy después de un fallo aleatorio (el servidor del marketplace podría simplemente haberse congelado por un segundo), pero rápidamente eliminar aquellas IP que sistemáticamente devuelven basura.
La lógica es simple: una solicitud exitosa con código 200 y contenido válido suma +2 al score (pero no más de 100). Un timeout — menos 15. Código 403/429 — menos 25. CAPTCHA en la respuesta — menos 20. Si el score cae por debajo de un umbral (por ejemplo, 30), el proxy entra en cuarentena por un tiempo fijo, y si después de volver de la cuarentena el score vuelve a caer por debajo de 10 — la IP se elimina del pool de forma definitiva y se registra para análisis.
Este enfoque es similar al sistema de reputación en navegadores anti-detect como Dolphin Anty o AdsPower, donde las cuentas tienen "salud" dependiendo de la actividad. La misma mecánica funciona muy bien para direcciones IP en scraping.
Código: ProxyPool con filtrado automático en Python
A continuación, un marco de trabajo para gestionar un pool de proxies teniendo en cuenta las métricas de errores. Se puede conectar a cualquier parser en requests, httpx o Scrapy, reemplazando la lógica de obtención de proxies por la llamada pool.get_proxy().
import time
import random
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class ProxyStats:
address: str
score: int = 100
quarantine_until: float = 0.0
total_requests: int = 0
total_success: int = 0
consecutive_timeouts: int = 0
is_dead: bool = False
@property
def success_rate(self) -> float:
if self.total_requests == 0:
return 1.0
return self.total_success / self.total_requests
class ProxyPool:
def __init__(self, proxies: list[str],
min_score: int = 30,
dead_score: int = 10,
quarantine_seconds: int = 1800):
self.stats = {p: ProxyStats(address=p) for p in proxies}
self.min_score = min_score
self.dead_score = dead_score
self.quarantine_seconds = quarantine_seconds
def _is_available(self, stats: ProxyStats) -> bool:
if stats.is_dead:
return False
if stats.quarantine_until > time.time():
return False
return True
def get_proxy(self) -> Optional[str]:
available = [s for s in self.stats.values() if self._is_available(s)]
if not available:
return None
# ordenamos por score, tomamos uno aleatorio del top-30% para no golpear una IP
available.sort(key=lambda s: s.score, reverse=True)
top_pool = available[:max(1, len(available) // 3)]
return random.choice(top_pool).address
def report_success(self, proxy: str):
s = self.stats[proxy]
s.total_requests += 1
s.total_success += 1
s.consecutive_timeouts = 0
s.score = min(100, s.score + 2)
def report_timeout(self, proxy: str):
s = self.stats[proxy]
s.total_requests += 1
s.consecutive_timeouts += 1
s.score -= 15
if s.consecutive_timeouts >= 3:
self._quarantine(s)
self._check_dead(s)
def report_blocked(self, proxy: str, captcha: bool = False):
s = self.stats[proxy]
s.total_requests += 1
s.score -= 20 if captcha else 25
self._quarantine(s)
self._check_dead(s)
def _quarantine(self, s: ProxyStats):
if s.score < self.min_score:
s.quarantine_until = time.time() + self.quarantine_seconds
def _check_dead(self, s: ProxyStats):
if s.score <= self.dead_score:
s.is_dead = True
def stats_report(self) -> list[dict]:
return [
{
"proxy": s.address,
"score": s.score,
"success_rate": round(s.success_rate, 2),
"dead": s.is_dead,
}
for s in self.stats.values()
]
En un parser real, las llamadas report_success, report_timeout y report_blocked se insertan directamente en el manejador de respuesta después de cada solicitud HTTP. Ejemplo de integración con requests:
import requests
def fetch(url: str, pool: ProxyPool):
proxy = pool.get_proxy()
if not proxy:
raise RuntimeError("No hay proxies disponibles en el pool")
proxies = {"http": proxy, "https": proxy}
try:
resp = requests.get(url, proxies=proxies, timeout=8)
except requests.exceptions.Timeout:
pool.report_timeout(proxy)
return None
if resp.status_code in (403, 429):
pool.report_blocked(proxy)
return None
if "captcha" in resp.text.lower():
pool.report_blocked(proxy, captcha=True)
return None
pool.report_success(proxy)
return resp
Este enfoque alivia la carga del monitoreo manual: configuras los umbrales una vez, y luego el pool "se recupera" automáticamente, expulsando direcciones muertas y devolviendo de la cuarentena aquellas que se congelaron temporalmente.
Valores umbral para el bloqueo de IP
No existen cifras universales — los umbrales dependen de la plataforma y de la agresividad de su anti-bots. Pero hay referencias que funcionan bien en la práctica al hacer scraping de Wildberries, Ozon y Avito:
- Tasa de éxito por debajo del 70% en una muestra de 30-50 solicitudes — motivo para eliminar la IP, incluso si el score actual no es crítico.
- 3 timeouts consecutivos — casi siempre significa que el proxy está físicamente inaccesible o sobrecargado, no tiene sentido mantenerlo en el pool activo.
- CAPTCHA más de 1 vez cada 20 solicitudes — la IP ya está bajo sospecha del anti-bots, es mejor reducir la frecuencia de solicitudes o cambiar la dirección.
- Tiempo promedio de respuesta superior a 2x la mediana del pool — señal de degradación, no crítica, pero motivo para bajar la prioridad al elegir.
La cuarentena de 30-60 minutos suele ser suficiente para proxies de centros de datos y residenciales, para que el anti-bots "olvide" la actividad sospechosa temporal. Para proxies móviles, la situación es diferente: allí la IP cambia al cambiar la celda del operador, por lo que la cuarentena puede ser más corta — 10-15 minutos, ya que después de la rotación es físicamente otra dirección.
Healthcheck y rotación en segundo plano
Además del filtrado reactivo (basado en errores en el flujo principal de scraping), es útil ejecutar un healthcheck en segundo plano — un proceso separado que cada pocos minutos verifica los proxies con solicitudes ligeras a una URL neutral (por ejemplo, httpbin.org/ip) y actualiza las estadísticas del pool antes de que el proxy llegue a una solicitud de producción a Wildberries o Ozon.
import threading
def healthcheck_loop(pool: ProxyPool, interval: int = 300):
while True:
for proxy in list(pool.stats.keys()):
try:
r = requests.get(
"https://httpbin.org/ip",
proxies={"http": proxy, "https": proxy},
timeout=5
)
if r.status_code == 200:
pool.report_success(proxy)
else:
pool.report_blocked(proxy)
except requests.exceptions.RequestException:
pool.report_timeout(proxy)
time.sleep(interval)
threading.Thread(target=healthcheck_loop, args=(pool,), daemon=True).start()
Este proceso en segundo plano es especialmente importante si el pool es grande (cientos de IPs) y el scraping no se realiza de manera continua, sino en lotes — sin healthcheck, parte de los proxies simplemente logra "caducar" entre ejecuciones, y los primeros minutos de scraping se gastan en el filtrado en vivo, en lugar de trabajar directamente con direcciones verificadas.
Qué tipo de proxy elegir para el scraping de marketplaces
La elección del tipo de proxy influye directamente en la frecuencia con la que se deberá ejecutar el filtrado y cuán agresivos serán los umbrales. Para un scraping estable de Wildberries y Ozon, donde el anti-bots analiza patrones de comportamiento y la reputación de la subred, los proxies residenciales son los que mejor funcionan — sus IP pertenecen a proveedores reales de internet doméstico, y el anti-bots bloquea mucho menos frecuentemente en lotes por subred.
Para fuentes más simples y menos protegidas (por ejemplo, la recolección de datos públicos de Avito o el monitoreo de precios en vitrinas simples) a menudo son suficientes proxies de centros de datos — son más rápidos y baratos, lo cual es crítico en grandes volúmenes de solicitudes, pero también son bloqueados un poco más frecuentemente, por lo que los umbrales de filtrado para ellos deben ser más estrictos.
| Tipo de proxy | Velocidad de filtrado | Cuándo usar |
|---|---|---|
| Centro de datos | Alta, subredes son bloqueadas en lotes | Vitrinas simples, grandes volúmenes, baja protección |
| Residencial | Media, IP son bloqueadas de forma puntual | Wildberries, Ozon, sitios con anti-bots |
| Móvil | Baja, IP cambia automáticamente por el operador | Fuentes altamente protegidas, scraping de alta frecuencia |
En la práctica, muchos equipos utilizan un pool híbrido: la mayoría de las solicitudes se realizan a través de IPs residenciales, y en caso de bloqueos masivos se conectan proxies móviles como reserva "de emergencia", ya que su cambio de IP ocurre de manera natural a través de la rotación de la red celular y no requiere intervención manual.
Conclusión
Un pool de proxies sin un sistema de filtrado inevitablemente se degrada, y cuanto más tiempo funcione sin limpieza, más recursos desperdicia el parser en IPs muertas. Un modelo de scoring que tenga en cuenta los códigos HTTP, timeouts, CAPTCHA y la tasa de éxito permite identificar automáticamente direcciones problemáticas y mantener el pool en estado operativo sin control manual. El código de este artículo se puede adaptar a cualquier stack — requests, httpx, Scrapy o su propio parser asíncrono.
Si solo estás formando un pool para el scraping de marketplaces y deseas reducir la frecuencia de bloqueos desde el inicio, recomendamos considerar proxies residenciales — ofrecen una tasa de éxito notablemente más alta en plataformas protegidas como Wildberries y Ozon en comparación con las direcciones de centros de datos comunes.