Conhecimento

Como Fazer a Rotação de Proxies Residenciais no Scrapy com Middleware Personalizado

O Scrapy define o proxy via meta, mas a armadilha do cache do Proxy-Authorization quebra a rotação. Um middleware personalizado que faz a rotação de identidade e tenta novamente em caso de bloqueios.

Chris Collins

Chris Collins

8 de agosto de 2026 · 8 min de leitura

Scrapy é o framework que você usa quando um scrape ultrapassa o tamanho de um script: ele lida com agendamento, concorrência, retentativas e pipelines prontos para uso. Adicionar um proxy residencial é simples, mas o Scrapy faz isso de forma diferente de um cliente HTTP comum. O proxy é uma configuração por requisição, tratada por um downloader middleware, e essa arquitetura tem uma armadilha específica em torno da autenticação que silenciosamente quebra a rotação. Entenda o modelo de middleware e tudo se encaixa.

Esta é a entrada sobre Scrapy ao lado de proxies residenciais com Python, que aborda requests e httpx. O pipeline de downloader-middleware do Scrapy é uma fera diferente, então merece um tratamento à parte aqui.

Tudo abaixo usa o gateway residencial da Shifter: um único endpoint, p.shifter.io:443, com toda a segmentação codificada no nome de usuário. Troque o host e as credenciais por outro provedor; a estrutura é a mesma.

O modelo de gateway em um parágrafo

O nome de usuário do proxy carrega sua autenticação e sua segmentação. Você não troca de endpoint para mudar de país ou sessão, você muda a string do nome de usuário:

customer-USERNAME-country-us-sid-abc123-ttl-600

country-us direciona para os Estados Unidos, sid fixa uma sessão persistente, ttl mantém esse IP por N segundos. Omita sid/ttl e cada nova conexão faz a rotação. A senha permanece constante. No Scrapy, esse nome de usuário se torna o cabeçalho Proxy-Authorization, e fazer a rotação de identidade significa alterá-lo a cada requisição.

Como o Scrapy lida com proxies

O Scrapy roteia o proxy de cada requisição através do HttpProxyMiddleware embutido, que lê request.meta['proxy']. A configuração ingênua é colocar as credenciais diretamente nessa URL:

# The tempting one-liner. It works, until you rotate.
request.meta['proxy'] = 'http://customer-USER-country-us:PASS@p.shifter.io:443'

Isso funciona para uma única identidade fixa. Quebra no momento em que você tenta fazer a rotação, e o motivo é a armadilha que vale a pena conhecer.

A armadilha: o Proxy-Authorization fica em cache

Quando o HttpProxyMiddleware vê credenciais na URL do proxy, ele as codifica em base64 em um cabeçalho Proxy-Authorization e, crucialmente, armazena esse cabeçalho em cache na requisição. Se uma requisição for posteriormente retentada ou redirecionada e seu meta['proxy'] mudar para uma identidade diferente, o middleware nem sempre recalcula o cabeçalho, então a requisição sai com um Proxy-Authorization obsoleto referente ao nome de usuário anterior. Em um gateway onde o nome de usuário carrega sua geolocalização e sessão, isso significa que sua rotação silenciosamente não roda: você muda o nome de usuário em meta['proxy'], mas a requisição ainda se autentica como a antiga.

A correção é parar de colocar credenciais na URL do proxy. Defina o host do proxy sem informações de usuário, e defina o cabeçalho Proxy-Authorization você mesmo, explicitamente, em cada requisição. É exatamente para isso que serve um middleware personalizado.

Um middleware de rotação personalizado

Coloque o host em meta['proxy'] sem credenciais, e calcule o cabeçalho de autenticação por requisição a partir da identidade que você deseja. Como a segmentação vive no nome de usuário, escolher um país e uma sessão é apenas construir o nome de usuário certo.

middlewares.py
import os
from w3lib.http import basic_auth_header
class ShifterProxyMiddleware:
def __init__(self):
self.user = os.environ['SHIFTER_USER']
self.password = os.environ['SHIFTER_PASS']
self.endpoint = 'http://p.shifter.io:443' # host only, no credentials
def process_request(self, request, spider):
country = request.meta.get('country', 'us')
sid = request.meta.get('sid') # set for a sticky session, omit to rotate
username = f"{self.user}-country-{country}" + (f"-sid-{sid}-ttl-600" if sid else "")
request.meta['proxy'] = self.endpoint
request.headers['Proxy-Authorization'] = basic_auth_header(username, self.password)

Ative-o, e deixe-o rodar antes do middleware de proxy embutido para que o cabeçalho que você definiu seja o que é enviado:

settings.py
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.ShifterProxyMiddleware': 350, # before HttpProxyMiddleware (750)
}

Agora cada requisição carrega seu próprio Proxy-Authorization recém-calculado, então alterar country ou sid no meta de uma requisição realmente muda a identidade. Defina sid nas requisições que pertencem a uma unidade lógica de trabalho para que compartilhem um IP, e deixe de fora para fazer a rotação por conexão (sticky vs rotating aborda essa distinção). Mapear trabalho para identidades dessa forma é o padrão de balanceamento de carga na forma do Scrapy.

Faça a rotação na retentativa, não só na programação

O RetryMiddleware do Scrapy já retenta timeouts e 5xxs, mas por padrão ele retenta com a mesma identidade, o que não faz sentido se o motivo da falha foi essa identidade ser bloqueada. A jogada de alto valor é fazer a rotação de identidade especificamente quando uma requisição falha ou volta desafiada. No seu middleware, detecte um bloqueio leve ou um 403/429 e reagende a requisição com uma nova identidade:

def process_response(self, request, response, spider):
if response.status in (403, 429) or looks_blocked(response):
new = request.copy()
new.meta.pop('sid', None) # drop the burned session -> fresh IP
new.dont_filter = True
return new # retry through a new identity
return response

Detectar o bloqueio leve é uma disciplina em si mesma, um 200 ainda pode ser uma página de bloqueio, então combine isso com as verificações em detectando conteúdo bloqueado ou falso. Tratar uma resposta desafiada como um sinal para fazer a rotação, em vez de aceitá-la, é o que mantém um crawl longo vivo.

Use os controles de cortesia do Scrapy

O Scrapy oferece os controles de limitação de taxa que um scraper feito à mão precisa construir, e com uma frota de proxies eles importam mais, não menos. Limite a concorrência por domínio para que um alvo não seja martelado, adicione um atraso, e ative o AutoThrottle para se adaptar às respostas do site:

settings.py
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 8 # per-target cap, the one that matters
DOWNLOAD_DELAY = 0.5
AUTOTHROTTLE_ENABLED = True
RETRY_ENABLED = True
RETRY_TIMES = 3

A concorrência por domínio é o controle que evita transformar um pool de proxies em um martelo distribuído. Mais paralelismo além da tolerância de um alvo compra bloqueios, não throughput (como evitar ser bloqueado e scraping responsável se aplicam), e o AutoThrottle recuando em respostas lentas é exatamente a contenção que um crawl saudável de longa duração precisa.

Verifique se você realmente está no proxy

Aponte um spider para um endpoint de eco de IP e verifique o IP de saída antes de confiar em uma execução:

def start_requests(self):
yield scrapy.Request('http://ip-api.com/json',
meta={'country': 'us'},
callback=self.parse) # expect a US residential IP

Seu próprio IP significa que o middleware não está sendo aplicado, ou está ordenado depois do HttpProxyMiddleware. Uma parede de timeouts significa que o cabeçalho de autenticação está errado ou ausente. Ambos são abordados no guia de diagnóstico de timeout.

Perguntas frequentes

Por que minha rotação de proxy não roda de verdade no Scrapy? Quase certamente é a armadilha do cache do Proxy-Authorization: você colocou credenciais na URL do proxy, e o HttpProxyMiddleware armazenou o cabeçalho de autenticação em cache, então quando você muda meta['proxy'] em uma retentativa a requisição ainda envia as credenciais antigas. Defina o host sem credenciais e calcule o cabeçalho Proxy-Authorization você mesmo em um middleware, a cada requisição.

Onde ficam as flags de segmentação? No nome de usuário, que se torna o Proxy-Authorization. Um middleware personalizado constrói customer-USER-country-<cc>-sid-<id>-ttl-<sec> a partir do meta de cada requisição, então escolher geolocalização e sessão é apenas definir country e sid na requisição.

Como dar a uma requisição específica uma sessão persistente? Defina um sid estável no meta dessa requisição e reutilize-o nas requisições que pertencem juntas; omita sid para fazer a rotação a cada conexão. O middleware transforma isso no nome de usuário correto.

Devo fazer a rotação a cada requisição ou na retentativa? Ambos têm seu lugar. Faça a rotação por unidade lógica de trabalho para o tráfego normal, e adicionalmente force uma identidade nova quando uma requisição voltar bloqueada ou limitada por taxa, para que um IP queimado não seja retentado como ele mesmo.

Ainda preciso de DOWNLOAD_DELAY e AutoThrottle com proxies rotativos? Sim. A rotação distribui a carga entre IPs, mas a concorrência por domínio, o atraso e o AutoThrottle evitam que você sobrecarregue um único alvo, independentemente de quantos IPs você tenha. Cortesia e rotação resolvem problemas diferentes.

Resumo

Scrapy com proxies residenciais é poderoso assim que você contorna sua única armadilha: não coloque credenciais na URL do proxy, porque o Proxy-Authorization em cache silenciosamente anula a rotação. Em vez disso, escreva um pequeno downloader middleware que defina o host em meta['proxy'] e calcule o cabeçalho Proxy-Authorization por requisição a partir da identidade que você deseja, faça a rotação dessa identidade por unidade lógica de trabalho e novamente em qualquer resposta bloqueada ou limitada por taxa, e conte com a concorrência por domínio e o AutoThrottle do Scrapy para manter a cortesia.

Faça isso e o agendador, as retentativas e os pipelines do Scrapy trabalham com sua camada de proxy em vez de contra ela. Aponte o crawl para o gateway residencial, e lembre-se de que a qualidade do pool decide com que frequência você precisa retentar (reputação de IP). A página de preços tem os planos por GB para testar contra seus próprios alvos.

Pronto para começar?

Experimente os proxies residenciais da Shifter, mais de 205M IPs, mais de 195 países, a partir de $0,75/GB.

Começar