Tu spider de Scrapy vuela en 100 páginas de prueba y luego se desmorona en 10.000. En realidad, casi nunca es un fallo de scraping: es un problema de red disfrazado de bug de scraping. La solución a la que todo el mundo recurre es un proxy, y meterlo en request.meta te lleva apenas treinta segundos.
El problema es que esa solución rápida es justo donde se quedan la mayoría de los tutoriales básicos. Te enseñan meta={"proxy": "http://IP:PORT"}, quizá una clase middleware, y ya. Pero suelen saltarse qué pasa cuando ese proxy se cae a mitad del rastreo, cómo evitar que las credenciales terminen en el historial de Git o por qué un reintento puede volver a usar el mismo proxy fallido. Esas son precisamente las preocupaciones de producción que cubre esta guía: configuración a prueba de fallos, gestión segura de secretos, selección intencional de proxy en reintentos, límites de protocolo y trade-offs de coste que sí se pueden medir.
¿Qué es un proxy middleware en Scrapy?
Un proxy middleware es una pieza de código que se coloca en la canalización de descarga de Scrapy y decide desde qué dirección IP debería parecer que sale una petición antes de tocar la red. Scrapy incluye uno integrado, HttpProxyMiddleware, que lee la clave proxy de request.meta, añade autenticación si hace falta y pasa la petición al handler de descarga que abre la conexión real. El middleware de proxy personalizado no sustituye ese paso de transporte: actúa como un selector que decide qué proxy se inserta antes de que el mecanismo integrado tome el relevo. Esa diferencia importa mucho más de lo que parece, y es la raíz de la mayoría de los errores del tipo “mi middleware personalizado no funciona”.
Por qué los proxies importan en proyectos Scrapy de producción
Un proxy cambia la ruta de red y la IP de origen aparente. Eso puede servir para pruebas geográficas legítimas, repartir tráfico de peticiones autorizado y aislar fallos de red. No da permisos, no se salta límites de velocidad ni garantiza acceso. Que un rastreo necesite proxy o no depende del destino, sus condiciones de uso, la tasa de peticiones y los requisitos de fiabilidad del trabajo.
No todos los proxies son iguales, y elegir la capa equivocada es otra forma de acabar en un incidente de producción:
| Tipo de proxy | Fiabilidad | Velocidad | Riesgo de detección | Coste típico |
|---|---|---|---|---|
| Proxies públicos gratuitos | Muy variable | Variable | A menudo alto | Sin coste, pero con riesgo operativo y de seguridad importante |
| Proxies de datacenter | Depende del proveedor y del destino | A menudo rápidos | Depende del destino | Suelen cobrarse por GB o por IP |
| Proxies residenciales | Depende del proveedor y del destino | Variable | Depende del destino | Suelen cobrarse por GB |
| Proxies ISP | Depende del proveedor y del destino | Variable | Depende del destino | Específico de cada proveedor |
Conviene mencionar aparte los proxies gratuitos porque los riesgos medidos son serios. El estudio de 30 meses Free Proxies Unmasked siguió más de 640.000 direcciones de 11 proveedores: el 34,5% estuvo activa al menos una vez y 16.923 manipulaban contenido. Esa población justifica una advertencia de seguridad fuerte para listas públicas; no es una tasa de fallo aplicable a cualquier lista, pool de pago, destino o carga de trabajo.
Además, los proxies no se saltan mágicamente la detección moderna de bots. Servicios como Cloudflare puntúan las peticiones con docenas de señales —huellas TLS, coherencia de cabeceras, ejecución de JavaScript, patrones de comportamiento— donde la IP es solo una entrada más. Un proxy residencial limpio, pero con cabeceras incoherentes y sin cookie jar, seguirá siendo marcado. Conviene tener eso claro antes de montar toda una arquitectura alrededor de “solo rota la IP”.
Antes de empezar
- Dificultad: Intermedia
- Tiempo necesario: ~30–45 minutos para la configuración completa de producción, ~5 minutos para la prueba rápida
- Lo que necesitas: Python 3.9+, Scrapy instalado (esta guía se ha probado con Scrapy 2.17.0, lanzado en julio de 2026), un spider funcional creado con
scrapy startprojecty al menos un endpoint de proxy (una prueba gratuita de cualquier proveedor de proxies de datacenter sirve perfectamente para probar)
Paso 1: prueba un proxy con el parámetro Request Meta
La forma más rápida de comprobar que un proxy funciona es saltarte por completo la arquitectura de middleware y probarlo directamente.
El HttpProxyMiddleware integrado de Scrapy lee la clave proxy directamente desde request.meta y enruta la petición a través de ella. Sin cambios en settings, sin clase middleware: solo un argumento.
import scrapy
class ProxyTestSpider(scrapy.Spider):
name = "proxy_test"
start_urls = ["https://httpbin.org/ip"]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
meta={"proxy": "http://203.0.113.10:8080"},
callback=self.parse,
)
def parse(self, response):
self.logger.info(response.text)
Ejecuta esto con scrapy runspider proxy_test.py. Si el proxy funciona, httpbin.org/ip devolverá la IP del proxy en vez de la tuya; esa es tu confirmación. Si la respuesta se queda colgada y al final da un error TCP connection timed out, el proxy está caído o no responde, algo que —sorpresa— pasa más a menudo de lo que a los proveedores de proxies les gusta reconocer.
Este método va bien para spiders sueltos o pruebas rápidas. Se rompe en cuanto tienes más de un spider, porque entonces acabas copiando la misma cadena de proxy en cinco archivos distintos.
Paso 2: crea un proxy middleware personalizado
Para cualquier escenario que vaya más allá de un solo spider, conviene centralizar la lógica del proxy en un solo sitio. Crea una clase ProxyMiddleware en el archivo middlewares.py del proyecto:
from scrapy.exceptions import NotConfigured
class ProxyMiddleware:
def __init__(self, proxy_url):
self.proxy_url = proxy_url
@classmethod
def from_crawler(cls, crawler):
proxy_url = crawler.settings.get("PROXY_URL")
if not proxy_url:
raise NotConfigured("PROXY_URL es obligatorio; se rechaza el fallback silencioso a conexión directa")
return cls(proxy_url=proxy_url)
def process_request(self, request, spider):
if "proxy" not in request.meta:
request.meta["proxy"] = self.proxy_url
Y regístralo en settings.py:
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Fíjate en from_crawler en lugar de un __init__ normal. Este es el patrón que Scrapy realmente recomienda para leer settings, y es exactamente el mismo gancho que volveremos a usar en el Paso 4 cuando hablemos de secretos. La ventaja aquí es sencilla: cambias una sola configuración y todos los spiders del proyecto usan el nuevo proxy. Nada de buscar a mano en cinco archivos para cambiar una IP caída.
Paso 3: entiende el orden de ejecución del middleware para que tu proxy no deje de funcionar sin avisar
Esta es la parte que casi todos los demás tutoriales se saltan con un “pon la prioridad en 350 y confía en mí”. El downloader middleware en Scrapy sigue un orden concreto y predecible; si no lo entiendes, tu configuración de proxy generará errores que parecerán no tener nada que ver con los proxies.
Los hooks del lado de la petición (process_request) se ejecutan en orden ascendente de prioridad: primero el número más bajo. Los hooks del lado de la respuesta (process_response, process_exception) se ejecutan en orden descendente: primero el número más alto, desandando la cadena hacia abajo.
Flujo de petición (ascendente):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] TU PROXY MIDDLEWARE
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
Flujo de respuesta (descendente):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] TU PROXY MIDDLEWARE → [300] HttpAuth
→ [100] RobotsTxt → Spider
Aquí tienes la tabla real y actual de prioridades por defecto de los middlewares integrados de Scrapy (verificada con 2.17.0):
| Middleware | Prioridad por defecto |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
Cuando RetryMiddleware programa un reintento, copia la petición fallida —incluidos sus metadatos— y esa nueva petición vuelve a entrar en la cadena de middlewares de descarga. La relación numérica del selector con la prioridad 550 no garantiza rotación. La rotación solo ocurre cuando el código del selector reconoce el reintento y sobrescribe de forma deliberada el meta["proxy"] copiado. La prioridad 350 es un buen sitio para un selector porque se ejecuta antes del paso de transporte integrado en 750, pero por sí sola no es un mecanismo de rotación.

Errores comunes al ordenar el middleware
- Poner tu middleware con la misma prioridad que
HttpProxyMiddleware(750): esto crea una condición de carrera en la que el orden del diccionario de Scrapy, y no tu lógica, decide qué middleware procesa primero la petición. Síntoma: comportamiento del proxy intermitente e inexplicable. - Usar
setdefault()oif "proxy" not in request.metaen un selector rotatorio: la copia del reintento conserva el proxy anterior. Síntoma: cada reintento repite la misma ruta fallida. Solución: detecta el reintento (por ejemplo,retry_times > 0) y sustituye explícitamente el valor de proxy que controla el selector. - Desactivar por completo
HttpProxyMiddleware: algunos tutoriales te dicen que pongas"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": Noneporque “el middleware personalizado se encarga”. No es así: tu middleware personalizado es un selector, no una capa de transporte. Desactivar el integrado hace que nunca se añadan las cabeceras de autenticación del proxy y que tus peticiones salgan sin autenticarse (o ni siquiera salgan).
Paso 4: deja de codificar credenciales de proxy a mano
Cada tutorial de proxy para Scrapy que aparece alto en Google que he visto escribe http://username:password@proxy.example.com:8080 directamente en un archivo Python. Eso deja una credencial viviendo para siempre en tu historial de Git, apareciendo en cada clon, fork y volcado de logs si alguien se despista con print().
En realidad, hay tres niveles para hacerlo:
| Método | Seguridad | Flexibilidad | Mejor para |
|---|---|---|---|
| Codificado en spider/settings.py | Mala: los secretos viven en el repo | Baja | Solo pruebas locales rápidas |
Variable de entorno http_proxy (nativa de Scrapy) | Mejor: fuera del código | Baja (un solo proxy) | Pipelines de CI/CD, Docker |
Archivo .env + python-dotenv + from_crawler | La mejor: fuera del código, por entorno | Alta (varios proxies, rotación) | Scrapers de producción |
La tercera opción merece hacerse bien. Instala python-dotenv, crea un archivo .env y añádelo de inmediato a .gitignore —lo digo en serio, hazlo ahora:
PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080
Cárgalo al principio de settings.py:
from dotenv import load_dotenv
import os
load_dotenv()
PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASSWORD = os.getenv("PROXY_PASSWORD")
PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")
Luego léelo de forma segura dentro del middleware usando from_crawler; este es el patrón que resuelve exactamente la duda que he visto en foros, donde la gente pregunta “¿cómo configuro esto antes de ejecutar scrapy crawl si las credenciales cambian en cada ejecución?”:
import os
from urllib.parse import quote
class SecureProxyMiddleware:
def __init__(self, user, password, host, port):
self.user = quote(user, safe="")
self.password = quote(password, safe="")
self.host = host
self.port = port
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(
user=settings.get("PROXY_USER"),
password=settings.get("PROXY_PASSWORD"),
host=settings.get("PROXY_HOST"),
port=settings.get("PROXY_PORT"),
)
def process_request(self, request, spider):
proxy_url = f"http://{self.user}:{self.password}@{self.host}:{self.port}"
request.meta["proxy"] = proxy_url
Fíjate en la llamada a urllib.parse.quote() alrededor de las credenciales. Si tu contraseña contiene @, : o / —y los generadores de contraseñas adoran meter estos caracteres—, romperá el análisis de la URL a menos que se codifique primero. Es una solución de una sola línea que te ahorra una hora bastante incómoda depurando errores de “invalid proxy URL” que no tienen nada que ver con que el proxy sea realmente inválido.
Mantén las credenciales fuera de los logs y prueba la codificación percent-encoding con valores representativos, pero falsos. El tunneling HTTPS, SOCKS5 y las credenciales no latinas tienen límites distintos según el handler; no des por hecho que un patrón de autenticación funciona igual en todos sin una prueba de integración fija.

Paso 5: añade rotación de proxies
Un solo proxy —incluso uno bueno— haciendo 5.000 peticiones al mismo sitio acabará siendo detectado. Necesitas un pool.
Opción A — hacerlo tú mismo. Es realmente sencillo:
import random
class RotatingProxyMiddleware:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def process_request(self, request, spider):
request.meta["proxy"] = random.choice(self.proxy_pool)
Esto funciona para usos básicos, pero no tiene ni idea de qué proxies siguen vivos. Estás tirando los dados en cada petición.
Opción B — usar scrapy-rotating-proxies. Este paquete de terceros añade detección de baneos y backoff automático desde el principio:
pip install scrapy-rotating-proxies
Aquí conviene ser sincero: la última versión en PyPI es la 0.6.2, fechada en 2019, y el proyecto está marcado como Alpha. No significa necesariamente que esté roto con Scrapy moderno, pero “sin mantenimiento activo desde 2019” no es lo mismo que “probado para tráfico de producción en 2026”. Fija la versión, pruébalo contra tus sitios reales y no asumas que maneja endpoints de proxy autenticados: en gran medida, no lo hace.
Paso 6: construye un middleware de proxy tolerante a fallos (detección de proxies caídos)
Esta es la sección que todos los tutoriales rivales se saltan por completo, y es lo que separa una demo de algo que aguanta un rastreo de 6 horas sin supervisión.
| Escenario | Lo que muestran la mayoría de los tutoriales | Lo que añade este middleware |
|---|---|---|
| El proxy devuelve 407 | No se aborda | Lo trata como un fallo de autenticación del proxy |
| El destino devuelve 403/429 | A menudo se agrupan | Mantiene separada la retroalimentación de política/límites de la salud del proxy |
| El proxy agota el tiempo | No se aborda | Umbral de timeout configurable, caída de puntuación de salud |
| Todos los proxies están caídos | No se aborda | Fallback elegante o pausa del rastreo con advertencia registrada |
| Proxy intermitente | No se aborda | Periodo de enfriamiento antes de volver a añadirlo al pool |
import time
import random
from scrapy.exceptions import IgnoreRequest
class FaultTolerantProxyMiddleware:
MAX_FAILURES = 3
COOLDOWN_SECONDS = 300
def __init__(self, proxy_pool):
self.pool = {p: {"failures": 0, "banned_until": 0} for p in proxy_pool}
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def _healthy_proxies(self):
now = time.time()
return [p for p, s in self.pool.items() if s["banned_until"] < now]
def process_request(self, request, spider):
healthy = self._healthy_proxies()
if not healthy:
spider.logger.warning("Todos los proxies están en mal estado: pausando el rastreo")
raise IgnoreRequest("No hay proxies sanos disponibles")
request.meta["proxy"] = random.choice(healthy)
def process_response(self, request, response, spider):
proxy = request.meta.get("proxy")
if proxy and response.status == 407:
self._mark_failure(proxy)
return response
def process_exception(self, request, exception, spider):
proxy = request.meta.get("proxy")
if proxy:
self._mark_failure(proxy)
def _mark_failure(self, proxy):
state = self.pool[proxy]
state["failures"] += 1
if state["failures"] >= self.MAX_FAILURES:
state["banned_until"] = time.time() + self.COOLDOWN_SECONDS
state["failures"] = 0
Un par de notas de haber construido esto de verdad: no trates cualquier 403 como prueba de que el proxy está muerto —RFC 9110 define 403 como “el servidor entendió la petición pero se niega a cumplirla”, lo que también puede significar que tus cabeceras o tu sesión parecen sospechosas, aunque el proxy no tenga la culpa. En cambio, un 407 sí significa que el proxy está rechazando específicamente tu autenticación: es una señal más fuerte y más concreta. Mezclar ambas señales en el mismo saco es la forma más rápida de quemar proxies sanos sin motivo.
Deja explícitos los retries transitorios por defecto de Scrapy en producción para que los revisores vean la política:
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2
Esos son los valores por defecto de Scrapy 2.17. No añadas 403 o 407 a lo loco: 403 es una negativa del destino con varias causas posibles, mientras que 407 es un problema de autenticación del proxy que un reintento genérico no va a resolver. Si un contrato concreto con un destino justifica reintentar otro estado, documenta ese motivo y pruébalo por separado.

Paso 7: un patrón medido de proxy en reintentos
Algunos destinos autorizados pueden devolver contenido válido directamente y solo necesitar un proxy tras una respuesta transitoria documentada. Eso puede reducir los bytes que pasan por proxy, pero no existe un porcentaje universal de ahorro defendible ni una tasa portátil de éxito directo. Mide tu propia carga de trabajo antes de adoptar el patrón.
Usa el helper público get_retry_request() de Scrapy y mantén la escalada limitada a estados específicos del destino que hayas clasificado explícitamente. Este ejemplo trata 429 y 503 como señales de presión que justifican reintento; excluye de forma intencional 403 y 407.
from scrapy.downloadermiddlewares.retry import get_retry_request
class CostAwareEscalationMiddleware:
def process_response(self, request, response, spider):
if response.status not in {429, 503}:
return response
retry = get_retry_request(
request,
spider=spider,
reason=f"proxy_escalation_{response.status}",
max_retry_times=2,
)
if retry is None:
return response
current_tier = request.meta.get("proxy_tier", "direct")
if current_tier == "direct":
retry.meta["proxy"] = spider.settings["DATACENTER_PROXY"]
retry.meta["proxy_tier"] = "datacenter"
elif current_tier == "datacenter":
retry.meta["proxy"] = spider.settings["RESIDENTIAL_PROXY"]
retry.meta["proxy_tier"] = "residential"
else:
return response
return retry
Registra la tasa de contenido válido en acceso directo, la tasa de contenido válido vía proxy, los bytes por registro satisfactorio, los reintentos por éxito y la latencia añadida. Un diseño direct-first solo es aceptable cuando el acceso directo está autorizado y el sistema falla de forma cerrada siempre que se requiera un proxy. El ahorro es la reducción medida del tráfico pasado por proxy, no un porcentaje supuesto.
Cuándo no compensa gestionar proxies por tu cuenta
Quiero ser claro con esto en vez de fingir que todo lo anterior fue inútil: todo lo de arriba es ingeniería real y útil, y para muchos proyectos —rastreos de gran volumen, pipelines personalizados, cualquier caso en el que necesites control total sobre la programación de peticiones— es la decisión correcta.
Pero si tu objetivo real es “obtener datos estructurados de esta página” y no “operar infraestructura de proxies”, una API de extracción puede ser una mejor frontera. La documentación actual de POST /extract de Thunderbit acepta una URL de página y un JSON Schema opcional; si omites el schema, el servicio puede generarlo a partir del contenido de la página. El endpoint también ofrece modos de renderizado none, basic y full, junto con controles de timeout y espera después de la carga. La extracción basada solo en prompts no forma parte del contrato de solicitud actualmente admitido, así que construye las integraciones de producción sobre el schema documentado. Esto mueve la interfaz de extracción detrás de una sola petición; no justifica una promesa universal sobre cualquier objetivo con anti-bot o CAPTCHA.
| Factor | Scrapy + proxies por tu cuenta | Extracción basada en API (por ejemplo, Thunderbit) |
|---|---|---|
| Esfuerzo de configuración | Alto: middleware, rotación, lógica de reintentos | Bajo: una llamada API con un schema |
| Comportamiento de red/renderizado | Tú configuras handlers, proxies, cabeceras y delays | Controlado mediante opciones documentadas de la API |
| Mantenimiento | Tú te encargas de selectores, salud del pool y cambios del destino | Tú te encargas de la calidad del schema, validación y comportamiento de integración |
| Modelo de costes | Tarifas de proxy + computación + tiempo de ingeniería | La documentación actual indica 20 unidades por página (verificado 2026-08-10) |
| Control | Total: pipelines personalizados, cadena de middleware | Limitado a las capacidades de la API |
| Mejor para | Rastreo complejo, gran volumen, lógica personalizada | Extracción concreta, prototipado, enriquecimiento |
Si lo que haces es extraer páginas estructuradas para listas de leads, datos de producto o investigación, ejecuta una prueba piloto representativa con la extensión de Chrome de Thunderbit o con la API y compara registros válidos, latencia, unidades y tiempo de mantenimiento. Si tu proyecto necesita grafos de rastreo personalizados y control del pipeline, Scrapy sigue siendo la opción más sólida.
Saber más
Consejos y errores comunes
- Consejo: prueba los proxies contra
httpbin.org/ipantes de apuntarlos a un objetivo real. Es la forma más rápida de confirmar que el enrutamiento funciona antes de añadir complejidad encima. - Error: poner el proxy en
request.headersen lugar de enrequest.meta. Es un fallo muy común por despiste:HttpProxyMiddlewaresolo leemeta["proxy"], y un intento basado en cabeceras fallará en silencio, sin un error obvio. - Error: asumir que los proxies HTTP y HTTPS se configuran igual. Una URL de proxy HTTP que enruta a un destino HTTPS normalmente funciona mediante túnel CONNECT con un handler compatible, pero usar el esquema
https://para el propio endpoint del proxy es una configuración distinta y menos soportada; no las confundas. - Consejo: si necesitas soporte SOCKS5, comprueba primero las capacidades del download handler de tu versión de Scrapy. El
Httpxhandler experimental de Scrapy 2.17 añadió soporte SOCKS5 mediantehttpx[socks], pero sigue marcado como experimental: no bases una dependencia de producción en él sin probarlo tú mismo.
Métodos alternativos
Además de scrapy-rotating-proxies, algunos equipos pasan toda la lógica de proxy a través de un gateway del proveedor: una única URL de proxy donde el vendor gestiona la rotación, la persistencia de sesión y la geo-selección por detrás. Esto sacrifica algo de control a cambio de bastante menos código de middleware, y merece la pena presupuestarlo frente a un pool autogestionado antes de construir uno desde cero.
Conclusión
Configurar un proxy en Scrapy lleva una línea. Hacer que esa configuración sobreviva a un rastreo real de producción exige una política de fallo explícita, credenciales protegidas, límites de handler probados y código selector que reemplace deliberadamente los metadatos de proxy copiados en los reintentos. Si te quedas con dos hábitos, que sean estos: fallar de forma cerrada cuando se necesite un proxy y no comprometer nunca una contraseña de proxy en un archivo Python.
Preguntas frecuentes
¿Cómo configuro un proxy personalizado en Scrapy con autenticación?
Usa el formato de URL protocol://username:password@host:port, pero codifica primero el nombre de usuario y la contraseña con urllib.parse.quote() si contienen caracteres especiales. En producción, lee esas credenciales mediante un método de clase from_crawler que tome los valores de variables de entorno, en lugar de escribirlas directamente en el código.
¿Qué número de prioridad debo usar para mi proxy middleware personalizado en Scrapy?
350 es una prioridad habitual para un selector porque se ejecuta antes de HttpProxyMiddleware en 750. No garantiza rotación. Un reintento recibe un proxy nuevo solo si el selector reconoce la petición copiada y sobrescribe su valor anterior de meta["proxy"].
¿Cómo gestiono automáticamente los proxies caídos en Scrapy?
Crea un middleware que lleve la cuenta de fallos por proxy en process_response y process_exception, retire los proxies del pool activo tras superar un umbral de fallos y los vuelva a añadir después de un periodo de enfriamiento, en vez de banearlos permanentemente.
¿Puedo usar Scrapy con proxies SOCKS5?
El HttpxDownloadHandler experimental de Scrapy 2.17 documenta soporte SOCKS5 cuando está instalado httpx[socks]. El handler HTTP/1.1 por defecto no soporta proxies SOCKS. Fija la versión del handler y prueba la integración antes de tratar esta ruta como lista para producción.
¿Cuánto puede ahorrar de verdad el patrón de proxy en reintentos? No existe un porcentaje portable. Mide la proporción de peticiones autorizadas que devuelven contenido válido directamente, los bytes enviados por cada nivel de proxy, los reintentos por cada registro exitoso y la latencia añadida. La reducción observada en bytes pasados por proxy es tu ahorro; si el acceso directo no está autorizado o no es válido, no uses este patrón.


