Seu spider do Scrapy voa em 100 páginas de teste e depois desmorona em 10.000. Isso não é exatamente um bug de scraping — é um problema de rede disfarçado de bug de scraping. A saída que todo mundo tenta é usar um proxy, e colocar um em request.meta leva coisa de trinta segundos.
O problema é que essa solução de trinta segundos é justamente onde muitos tutoriais básicos param. Eles mostram meta={"proxy": "http://IP:PORT"}, talvez uma classe de middleware, e pronto. Quase nunca explicam o que acontece quando esse proxy cai no meio do crawl, como credenciais acabam no histórico do Git, ou por que uma nova tentativa pode reutilizar o mesmo proxy que falhou. É disso que este guia trata: configuração com falha segura, gestão de segredos, seleção intencional de proxy em retentativas, limites de protocolo e trade-offs de custo que dá para medir.
O que é um Middleware de Proxy no Scrapy?
Um middleware de proxy é um pedaço de código que fica no pipeline de download do Scrapy e decide de qual endereço IP uma requisição deve parecer vir antes de ela sair pela rede. O Scrapy já traz um nativo, HttpProxyMiddleware, que lê a chave proxy de request.meta, adiciona autenticação de proxy quando necessário e entrega a requisição ao handler de download que realmente abre a conexão. Um middleware de proxy personalizado não substitui essa etapa de transporte — ele funciona como um seletor, escolhendo qual proxy será usado antes que a mecânica nativa assuma o controle. Essa diferença importa muito mais do que parece e é a origem da maioria dos relatos de "meu middleware personalizado não funciona".
Por que Proxies Importam em Projetos Scrapy de Produção
Um proxy altera a rota de rede e o IP aparente de origem. Isso pode ajudar em testes geográficos legítimos, distribuir tráfego de requisições autorizado e isolar falhas de rede. Ele não concede permissão, não burla limites de taxa e não garante acesso. Se um crawl precisa ou não de proxy depende do alvo, dos termos de uso, da taxa de requisições e dos requisitos de confiabilidade da tarefa.
Nem todo proxy é igual, e escolher a categoria errada é um tipo próprio de incidente de produção:
| Tipo de Proxy | Confiabilidade | Velocidade | Risco de Detecção | Custo Típico |
|---|---|---|---|---|
| Proxies públicos gratuitos | Muito variável | Variável | Frequentemente alto | Sem custo, mas com risco operacional e de segurança relevante |
| Proxies de datacenter | Depende do provedor e do alvo | Geralmente rápido | Depende do alvo | Normalmente cobrado por GB ou por IP |
| Proxies residenciais | Depende do provedor e do alvo | Variável | Depende do alvo | Normalmente cobrado por GB |
| Proxies ISP | Depende do provedor e do alvo | Variável | Depende do alvo | Específico do provedor |
Proxies gratuitos merecem um destaque especial porque os riscos medidos são altos. O estudo de 30 meses Free Proxies Unmasked acompanhou mais de 640.000 endereços de 11 provedores: 34,5% estiveram ativos ao menos uma vez, e 16.923 manipularam conteúdo. Esse resultado sustenta um alerta de segurança forte para listas públicas; ele não representa uma taxa de falha transportável para qualquer lista atual, pool pago, alvo ou carga de trabalho.
Proxies também não derrotam magicamente a detecção moderna de bots. Serviços como o Cloudflare avaliam requisições com dezenas de sinais — impressões TLS, consistência de headers, execução de JavaScript, padrões comportamentais — e o IP é só um dos insumos. Um proxy residencial limpo, usado em uma requisição com headers inconsistentes e sem cookie jar, ainda pode ser sinalizado. Ajuste essa expectativa antes de montar toda a arquitetura em cima de "é só rotacionar o IP".
Antes de Começar
- Dificuldade: Intermediária
- Tempo necessário: ~30–45 minutos para a configuração completa de produção, ~5 minutos para o teste rápido
- O que você vai precisar: Python 3.9+, Scrapy instalado (este guia foi testado com Scrapy 2.17.0, lançado em julho de 2026), um spider funcional criado com
scrapy startprojecte pelo menos um endpoint de proxy (uma avaliação grátis de qualquer provedor de proxy de datacenter já serve para testes)
Passo 1: Teste um Proxy com o Parâmetro request.meta
A forma mais rápida de confirmar se um proxy funciona é ignorar toda a arquitetura de middleware e testá-lo diretamente.
O HttpProxyMiddleware nativo do Scrapy lê a chave proxy diretamente de request.meta e roteia a requisição por ela. Sem mexer em settings, sem classe de middleware — apenas um argumento-chave.
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)
Execute com scrapy runspider proxy_test.py. Se o proxy funcionar, httpbin.org/ip vai retornar o IP do proxy em vez do seu — essa é a confirmação. Se a resposta travar e acabar em TCP connection timed out, o proxy está morto ou inacessível, o que, spoiler, acontece com muito mais frequência do que os fornecedores gostam de admitir.
Esse método é ótimo para spiders pontuais ou testes rápidos. Ele quebra assim que você tem mais de um spider, porque agora você passa a repetir a mesma string de proxy em cinco arquivos diferentes.
Passo 2: Crie um Middleware de Proxy Personalizado
Para qualquer cenário além de um único spider, vale centralizar a lógica de proxy em um só lugar. Crie uma classe ProxyMiddleware no arquivo middlewares.py do projeto:
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 é obrigatório; recusando fallback silencioso para conexão direta")
return cls(proxy_url=proxy_url)
def process_request(self, request, spider):
if "proxy" not in request.meta:
request.meta["proxy"] = self.proxy_url
E registre em settings.py:
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Repare no método de classe from_crawler em vez de um __init__ simples. Esse é o padrão que o próprio Scrapy recomenda para ler settings, e é o mesmo hook que vamos usar de novo no Passo 4, quando falarmos de segredos. A vantagem aqui é direta: você muda uma configuração e todos os spiders do projeto passam a usar o novo proxy. Nada de procurar manualmente em cinco arquivos para trocar um IP morto.
Passo 3: Entenda a Ordem de Execução dos Middlewares (Para Seu Proxy Não Parecer Que Não Faz Nada)
Aqui está a parte que quase todos os outros tutoriais pulam com um “é só colocar prioridade 350, confia”. O downloader middleware do Scrapy roda em uma ordem específica e previsível, e se você não entender isso, sua configuração de proxy vai gerar bugs que parecem não ter nada a ver com proxy.
Os hooks do lado da requisição (process_request) rodam em ordem crescente de prioridade — número menor primeiro. Os hooks do lado da resposta (process_response, process_exception) rodam em ordem decrescente — número maior primeiro, voltando pela cadeia.
Fluxo de requisição (crescente):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] SEU MIDDLEWARE DE PROXY
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
Fluxo de resposta (decrescente):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] SEU MIDDLEWARE DE PROXY → [300] HttpAuth
→ [100] RobotsTxt → Spider
Aqui está a tabela real e atual de prioridades padrão dos middlewares nativos do Scrapy (verificada na 2.17.0):
| Middleware | Prioridade padrão |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
Quando o RetryMiddleware agenda uma nova tentativa, ele copia a requisição com falha — incluindo os metadados — e essa nova requisição entra novamente na cadeia de downloader middleware. A relação numérica do seletor com a prioridade 550 não garante rotação. A rotação só acontece quando o código do seletor reconhece a retentativa e substitui de propósito o meta["proxy"] copiado. A prioridade 350 é um ponto conveniente para um seletor porque ela roda antes da etapa de transporte nativa em 750, mas sozinha não é um mecanismo de rotação.

Erros Comuns de Ordenação de Middleware
- Definir seu middleware com a mesma prioridade do
HttpProxyMiddleware(750): isso cria uma condição de corrida em que a ordem de iteração do dicionário do Scrapy — e não sua lógica — decide qual middleware processa a requisição primeiro. Sintoma: comportamento de proxy intermitente e inexplicável. - Usar
setdefault()ouif "proxy" not in request.metaem um seletor rotativo: a retentativa copiada mantém o proxy antigo. Sintoma: toda repetição usa a mesma rota que já falhou. Correção: detectar que é uma retry (por exemplo,retry_times > 0) e substituir explicitamente o valor de proxy pertencente ao seletor. - Desativar o
HttpProxyMiddlewarepor completo: alguns tutoriais dizem para definir"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": Noneporque “o middleware personalizado cuida disso”. Não cuida — seu middleware personalizado é um seletor, não uma camada de transporte. Desativar o nativo faz com que os headers de autenticação do proxy nunca sejam anexados, e as requisições saem sem autenticação (ou nem saem).
Passo 4: Pare de Colocar Credenciais de Proxy no Código
Todo tutorial de proxy para Scrapy que aparece no topo dos resultados escreve http://username:password@proxy.example.com:8080 direto em um arquivo Python. Isso é uma credencial que fica para sempre no seu histórico do Git, em cada clone, em cada fork e em qualquer dump de logs se alguém abusar de print().
Na prática, existem três níveis para fazer isso:
| Método | Segurança | Flexibilidade | Melhor para |
|---|---|---|---|
| Codificado em spider/settings.py | Ruim — segredos ficam no repositório | Baixa | Apenas testes locais rápidos |
Variável de ambiente http_proxy (nativa do Scrapy) | Melhor — fora do código | Baixa (um proxy) | Pipelines de CI/CD, Docker |
Arquivo .env + python-dotenv + from_crawler | Melhor — fora do código, por ambiente | Alta (múltiplos proxies, rotação) | Scrapers de produção |
A terceira opção vale a pena ser feita direito. Instale python-dotenv, crie um arquivo .env e adicione-o imediatamente ao .gitignore — estou falando sério, faça isso agora:
PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080
Carregue isso no topo 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")
Depois, leia com segurança dentro do middleware usando from_crawler — esse é o padrão que resolve exatamente a dúvida que eu vejo em fóruns o tempo todo: “como defino isso antes de rodar scrapy crawl se as credenciais mudam a cada execução?”
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
Repare na chamada urllib.parse.quote() em volta das credenciais. Se sua senha contiver @, : ou / (e geradores de senha adoram usar esses caracteres), o parsing da URL quebra a menos que tudo seja percent-encoded antes. É uma correção de uma linha que poupa uma hora constrangedora depurando erros de “URL de proxy inválida” que não têm nada de errado com o proxy em si.
Mantenha credenciais fora dos logs e teste o percent-encoding com valores representativos — mas falsos. Encapsulamento HTTPS, SOCKS5 e credenciais com caracteres não latinos têm limites específicos por handler; não assuma que um padrão de autenticação funciona em todos eles sem um teste de integração fixado.

Passo 5: Adicione Rotação de Proxy
Um único proxy — mesmo que seja bom — fazendo 5.000 requisições para o mesmo site acaba sendo sinalizado em algum momento. Você precisa de um pool.
Opção A — construir você mesmo. É realmente simples:
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)
Isso funciona para uso básico, mas não sabe quais proxies estão vivos. Você está jogando dados a cada requisição.
Opção B — usar scrapy-rotating-proxies. Esse pacote de terceiros adiciona detecção de banimento e backoff automático pronto para uso:
pip install scrapy-rotating-proxies
Vou apontar algo com honestidade: a última versão no PyPI é a 0.6.2, datada de 2019, e o projeto está marcado como Alpha. Isso não significa necessariamente que está quebrado no Scrapy moderno, mas “não é mantido ativamente desde 2019” não é a mesma coisa que “testado em batalha para tráfego de produção em 2026”. Fixe a versão, teste contra os alvos reais e não presuma que ele lida com endpoints de proxy autenticados — em grande parte, não lida.
Passo 6: Crie um Middleware de Proxy Tolerante a Falhas (Detecção de Proxy Morto)
Esta é a seção que todo tutorial concorrente pula completamente, e é a diferença entre uma demo e algo que aguenta um crawl de 6 horas sem supervisão.
| Cenário | O que a maioria dos tutoriais mostra | O que este middleware adiciona |
|---|---|---|
| Proxy retorna 407 | Não aborda | Trata como falha de autenticação do proxy |
| O alvo retorna 403/429 | Frequentemente junta tudo | Mantém feedback de política/limite separado da saúde do proxy |
| Proxy expira | Não aborda | Limite configurável de timeout, degradação de score de saúde |
| Todos os proxies morrem | Não aborda | Fallback gracioso ou pausa do crawl com alerta registrado |
| Proxy oscila (intermitente) | Não aborda | Período de cooldown antes de voltar ao 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 os proxies estão com problema — pausando o crawl")
raise IgnoreRequest("Nenhum proxy saudável disponível")
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
Algumas observações de quem realmente construiu isso: não trate todo 403 como prova de que o proxy morreu — RFC 9110 define 403 como “o servidor entendeu, mas se recusa”, o que pode significar simplesmente que seus headers ou sua sessão parecem suspeitos, independentemente do proxy. Já um 407 significa que o próprio proxy está rejeitando sua autenticação — esse é um sinal mais forte e específico. Jogar esses sinais no mesmo saco é como queimar proxies saudáveis sem motivo.
Deixe explícitos os retries transitórios padrão do Scrapy em produção para que os revisores enxerguem a política:
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2
Esses são os padrões do Scrapy 2.17. Não adicione 403 ou 407 em massa: 403 é recusa do alvo com várias possíveis causas, enquanto 407 é um problema de autenticação do proxy que uma nova tentativa genérica não vai resolver. Se um contrato específico do alvo justificar a repetição de outro status, documente o motivo e teste separadamente.

Passo 7: Um Padrão Medido de Proxy-na-Retry
Alguns alvos autorizados podem retornar conteúdo válido diretamente e só precisarem de proxy depois de uma resposta transitória documentada. Isso pode reduzir bytes trafegados por proxy, mas não existe uma porcentagem universal defensável de economia nem uma taxa portátil de sucesso direto. Meça sua própria carga de trabalho antes de adotar esse padrão.
Use o helper público get_retry_request() do Scrapy e mantenha a escalada limitada a status específicos do alvo que você classificou explicitamente. Este exemplo trata 429 e 503 como sinais de pressão que justificam retry; ele exclui intencionalmente 403 e 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
Registre taxa de conteúdo válido em acesso direto, taxa de conteúdo válido via proxy, bytes por registro bem-sucedido, retries por sucesso e latência adicional. Um desenho direto-primeiro só é aceitável quando o acesso direto é autorizado e o sistema falha com bloqueio quando um proxy é necessário. A economia é a redução medida no tráfego proxied — não uma porcentagem presumida.
Quando Vale Mais a Pena Não Gerenciar Proxy na Mão
Quero ser direto sobre isso em vez de fingir que todo o resto do artigo era inútil: tudo acima é engenharia real e útil, e para muitos projetos — crawls de alto volume, pipelines personalizados, qualquer coisa em que você precise de controle total sobre o agendamento das requisições — é a escolha certa.
Mas se seu objetivo real é "obter dados estruturados desta página" e não "operar infraestrutura de proxy", uma API de extração pode ser uma fronteira melhor. A documentação atual do POST /extract do Thunderbit aceita uma URL de página e um JSON Schema opcional; quando o schema é omitido, o serviço pode gerá-lo a partir do conteúdo da página. O endpoint também oferece modos de renderização none, basic e full, além de controles de timeout e espera após carregamento. Extração apenas por prompt não faz parte da superfície de requisição suportada no momento, então crie integrações de produção com base no contrato de schema documentado. Isso move a interface de extração para trás de uma única requisição; não autoriza uma promessa universal para qualquer alvo com anti-bot ou CAPTCHA.
| Fator | Scrapy + proxies por conta própria | Extração baseada em API (ex.: Thunderbit) |
|---|---|---|
| Esforço de configuração | Alto — middleware, rotação, lógica de retry | Baixo — uma chamada de API com schema |
| Comportamento de rede/renderização | Você configura handlers, proxies, headers e delays | Controlado pelas opções documentadas da API |
| Manutenção | Você é responsável pelos seletores, saúde do pool e mudanças do alvo | Você é responsável pela qualidade do schema, validação e comportamento da integração |
| Modelo de custo | Taxas de proxy + compute + tempo de engenharia | A documentação atual lista 20 unidades por página (verificado em 2026-08-10) |
| Controle | Total — pipelines e cadeia de middleware customizados | Limitado aos recursos da API |
| Melhor para | Crawls complexos, alto volume, lógica customizada | Extração direcionada, prototipagem, enriquecimento |
Se você está extraindo páginas estruturadas para listas de leads, dados de produto ou pesquisa, faça um piloto representativo com a Thunderbit Chrome Extension ou com a API e compare registros válidos, latência, unidades e tempo de manutenção. Se o seu projeto precisa de grafos de crawl personalizados e controle de pipeline, o Scrapy continua sendo a opção mais forte.
Saiba Mais
Dicas e Erros Comuns
- Dica: teste proxies em
httpbin.org/ipantes de apontá-los para um alvo real. É a forma mais rápida de confirmar que o roteamento funciona antes de adicionar complexidade em cima. - Erro comum: colocar o proxy em
request.headersem vez derequest.meta. Esse é um bug muito comum por um detalhe de digitação — oHttpProxyMiddlewaresó lêmeta["proxy"], e uma tentativa baseada em header falha silenciosamente, sem erro óbvio. - Erro comum: assumir que proxies HTTP e HTTPS são configurados da mesma forma. Uma URL de proxy HTTP roteando para um destino HTTPS normalmente funciona via tunneling CONNECT em um handler compatível, mas usar o esquema
https://para o endpoint do proxy é uma configuração diferente, menos suportada — não confunda as duas coisas. - Dica: se você precisa de suporte a SOCKS5, confira primeiro as capacidades do download handler da sua versão do Scrapy. O
HttpxDownloadHandlerexperimental do Scrapy 2.17 adicionou suporte a SOCKS5 viahttpx[socks], mas ele ainda é rotulado como experimental — não construa uma dependência de produção sobre isso sem seus próprios testes.
Métodos Alternativos
Além do scrapy-rotating-proxies, algumas equipes direcionam toda a lógica de proxy por meio de um gateway do provedor — uma única URL de proxy em que o fornecedor gerencia rotação, stickiness de sessão e geotargeting por trás das cenas. Isso troca um pouco de controle por bem menos código de middleware, e vale a pena comparar custos com um pool autogerenciado antes de construir tudo do zero.
Conclusão
Colocar um proxy no Scrapy leva uma linha. Fazer isso sobreviver a um crawl real de produção exige política explícita de falha, credenciais protegidas, limites de handler testados e código de seletor que substitua de forma deliberada os metadados copiados do proxy em retentativas. Se você levar duas regras para casa, que sejam estas: falhe com bloqueio quando um proxy for obrigatório e nunca comite uma senha de proxy em um arquivo Python.
Perguntas Frequentes
Como configuro um proxy personalizado no Scrapy com autenticação?
Use o formato de URL protocol://username:password@host:port, mas faça percent-encoding do nome de usuário e da senha com urllib.parse.quote() antes, caso contenham caracteres especiais. Em produção, leia essas credenciais por meio de um método de classe from_crawler alimentado por variáveis de ambiente, em vez de colocá-las diretamente no código.
Qual número de prioridade devo usar para meu middleware de proxy personalizado no Scrapy?
350 é uma prioridade comum para o seletor porque ele roda antes do HttpProxyMiddleware em 750. Isso não garante rotação. Uma retentativa só recebe um proxy novo se o seletor reconhecer a requisição copiada da retry e sobrescrever o valor anterior de meta["proxy"].
Como lidar automaticamente com proxies mortos no Scrapy?
Crie um middleware que acompanhe contagens de falha por proxy em process_response e process_exception, remova proxies do pool ativo depois de um limite de falhas e os reintroduza após um período de cooldown, em vez de bani-los permanentemente.
Posso usar o Scrapy com proxies SOCKS5?
O HttpxDownloadHandler experimental do Scrapy 2.17 documenta suporte a SOCKS5 quando httpx[socks] está instalado. O handler HTTP/1.1 padrão não suporta proxies SOCKS. Fixe a versão/handler e rode um teste de integração antes de tratar isso como pronto para produção.
Quanto o padrão de proxy-na-retry realmente pode economizar em custos de proxy? Não existe uma porcentagem portátil. Meça a parcela de requisições autorizadas que retornam conteúdo válido diretamente, os bytes enviados por cada camada de proxy, o número de retries por registro bem-sucedido e a latência adicional. A redução observada nos bytes proxied é a sua economia; se o acesso direto não for autorizado ou válido, não use esse padrão.


