Voltar ao blog

Configuração de proxy no framework Scrapy: guia completo com exemplos de código

Guia completo para a integração de proxies no Scrapy: desde a configuração básica até métodos avançados de rotação de IPs com exemplos de código funcional.

📅14 de fevereiro de 2026
```html

Scrapy é um dos frameworks Python mais poderosos para web scraping, mas sem a configuração correta de proxy, seus scrapers podem ser bloqueados em poucos minutos de operação. Neste guia, vou mostrar todas as maneiras de integrar proxies no Scrapy: desde a configuração mais simples até métodos avançados de rotação de endereços IP com tratamento automático de erros.

O material é baseado na experiência real de scraping de grandes plataformas de e-commerce e sites protegidos. Você receberá exemplos de código prontos para usar em seus projetos.

Por que Scrapy sem proxy recebe bloqueios

Sites modernos utilizam proteção em múltiplas camadas contra scraping. Mesmo que você tenha configurado o User-Agent e delays entre requisições, seu endereço IP pode ser identificado como automatizado por vários sinais:

  • Frequência de requisições: um IP fazendo 100+ requisições por minuto — um claro sinal de bot
  • Padrões de comportamento: navegação sequencial em páginas sem transições aleatórias
  • Ausência de JavaScript: Scrapy não executa JS, o que é facilmente detectável
  • Geolocalização: acesso a partir de um data center em vez de uma rede doméstica

O resultado é um banimento por IP por várias horas ou dias. Proteções especialmente agressivas são utilizadas por marketplaces (Amazon, Wildberries, Ozon), redes sociais e sites com Cloudflare. Proxies resolvem esse problema, distribuindo requisições entre múltiplos endereços IP.

Importante: Mesmo com proxies, é necessário respeitar os limites de taxa. A velocidade recomendada: 1-3 requisições por segundo por IP. Para scraping em alta velocidade, utilize um pool de 50+ proxies com rotação.

Configuração básica de proxy no Scrapy

A maneira mais simples é especificar o proxy diretamente nas configurações do spider. Este método é adequado para testes ou scraping de pequenos volumes de dados com um único servidor proxy.

Método 1: Através de meta em Request

import scrapy

class MySpider(scrapy.Spider):
    name = 'example'
    start_urls = ['https://example.com']
    
    def start_requests(self):
        proxy = 'http://username:password@proxy.example.com:8080'
        
        for url in self.start_urls:
            yield scrapy.Request(
                url=url,
                callback=self.parse,
                meta={'proxy': proxy}
            )
    
    def parse(self, response):
        # Sua lógica de scraping
        self.log(f'Scraped {response.url} via {response.meta["proxy"]}')

O formato do proxy depende do protocolo e do método de autenticação:

  • http://proxy.example.com:8080 — sem autenticação
  • http://user:pass@proxy.example.com:8080 — com login/senha
  • socks5://user:pass@proxy.example.com:1080 — proxy SOCKS5

Método 2: Configuração global em settings.py

# settings.py

# Proxy HTTP para todas as requisições
HTTPPROXY_ENABLED = True
HTTPPROXY_AUTH_ENCODING = 'utf-8'

# Configuração através de variáveis de ambiente
HTTP_PROXY = 'http://username:password@proxy.example.com:8080'
HTTPS_PROXY = 'http://username:password@proxy.example.com:8080'

Este método é conveniente para testes rápidos, mas não é adequado para produção: não há rotação de IP, se o proxy falhar, todo o scraper para, e não é possível usar diferentes proxies para diferentes sites.

Criando um Middleware de Proxy personalizado

Para scraping em produção, é necessário um middleware próprio que gerencie o pool de proxies, trate erros e troque IPs automaticamente. Aqui está uma implementação básica:

# middlewares.py

import random
from scrapy import signals
from scrapy.exceptions import NotConfigured

class RandomProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
    
    @classmethod
    def from_crawler(cls, crawler):
        # Carregamos a lista de proxies das configurações
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        
        if not proxy_list:
            raise NotConfigured('PROXY_LIST não configurada')
        
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Escolhemos um proxy aleatório do pool
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy
        
        spider.logger.info(f'Usando proxy: {proxy}')
    
    def process_exception(self, request, exception, spider):
        # Em caso de erro, tentamos outro proxy
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy
        
        spider.logger.warning(
            f'Erro no proxy, trocando para: {proxy}'
        )
        
        return request

Agora configuramos o uso do middleware em settings.py:

# settings.py

# Lista de proxies (pode ser carregada de um arquivo ou API)
PROXY_LIST = [
    'http://user1:pass1@proxy1.example.com:8080',
    'http://user2:pass2@proxy2.example.com:8080',
    'http://user3:pass3@proxy3.example.com:8080',
    # ... adicione 50+ proxies para rotação eficaz
]

# Conectando o middleware
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomProxyMiddleware': 350,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 400,
}

# Tentativas de repetição em caso de erros
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]

Rotação de proxies: três métodos funcionais

A escolha aleatória de proxies (como no exemplo acima) é a mais simples, mas não a mais eficaz. Vamos considerar três estratégias de rotação para diferentes cenários.

Método 1: Round-robin (rotação sequencial)

Proxies são escolhidos em círculo. Adequado para distribuir a carga uniformemente:

class RoundRobinProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.current_index = 0
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Pegamos o próximo proxy em círculo
        proxy = self.proxy_list[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxy_list)
        
        request.meta['proxy'] = proxy

Método 2: Rotação inteligente com blacklist

Monitoramos proxies problemáticos e os excluímos temporariamente da rotação:

import time
from collections import defaultdict

class SmartProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.proxy_errors = defaultdict(int)
        self.blacklist = set()
        self.blacklist_timeout = 300  # 5 minutos
        self.blacklist_time = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def get_working_proxies(self):
        # Remove da blacklist proxies cujo tempo de banimento expirou
        current_time = time.time()
        expired = [
            proxy for proxy, ban_time in self.blacklist_time.items()
            if current_time - ban_time > self.blacklist_timeout
        ]
        
        for proxy in expired:
            self.blacklist.discard(proxy)
            self.proxy_errors[proxy] = 0
        
        # Retorna proxies funcionais
        return [p for p in self.proxy_list if p not in self.blacklist]
    
    def process_request(self, request, spider):
        working_proxies = self.get_working_proxies()
        
        if not working_proxies:
            spider.logger.error('Todos os proxies estão na blacklist!')
            return
        
        proxy = random.choice(working_proxies)
        request.meta['proxy'] = proxy
    
    def process_response(self, request, response, spider):
        # Se recebemos um bloqueio — adicionamos à blacklist
        if response.status in [403, 429, 503]:
            proxy = request.meta.get('proxy')
            self.proxy_errors[proxy] += 1
            
            if self.proxy_errors[proxy] >= 3:
                self.blacklist.add(proxy)
                self.blacklist_time[proxy] = time.time()
                spider.logger.warning(
                    f'Proxy {proxy} banido por {self.blacklist_timeout}s'
                )
        
        return response

Método 3: Rotação através da API do provedor

Muitos provedores de proxy (incluindo proxies residenciais) oferecem um endpoint rotativo — uma URL que muda automaticamente o IP a cada requisição:

# settings.py

# Endpoint único com rotação automática
ROTATING_PROXY = 'http://username:password@rotating.proxy.com:8080'

# Middleware simples
class RotatingProxyMiddleware:
    def __init__(self, proxy):
        self.proxy = proxy
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy = crawler.settings.get('ROTATING_PROXY')
        return cls(proxy)
    
    def process_request(self, request, spider):
        # Uma URL, mas cada requisição vai com um novo IP
        request.meta['proxy'] = self.proxy

Este é o método mais conveniente para produção: não é necessário gerenciar um pool de proxies, o provedor cuida da qualidade dos IPs e substitui os problemáticos. Funciona especialmente bem com proxies residenciais, onde o pool de IPs pode atingir milhões de endereços.

Autenticação: login/senha vs lista de IPs permitidos

Provedores de proxy oferecem dois métodos de autenticação. A escolha afeta a velocidade de conexão e a conveniência da configuração.

Autenticação User:Pass

O login e a senha são passados na URL do proxy. O Scrapy automaticamente os converte em um cabeçalho HTTP Proxy-Authorization:

proxy = 'http://username:password@proxy.example.com:8080'
request.meta['proxy'] = proxy

# O Scrapy adicionará automaticamente o cabeçalho:
# Proxy-Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Prós: funciona de qualquer IP, fácil de trocar proxies no código.
Contras: pequeno overhead em cada requisição (~50-100ms), credenciais em texto claro no código.

Autenticação IP Whitelist

Você adiciona o IP do seu servidor na whitelist do provedor, não é necessária autenticação:

proxy = 'http://proxy.example.com:8080'  # sem login/senha
request.meta['proxy'] = proxy

Prós: mais rápido em 50-100ms, mais seguro (sem credenciais no código).
Contras: funciona apenas a partir de IPs específicos, é necessário atualizar a whitelist ao trocar de servidor.

Recomendação para produção:

Utilize a whitelist de IPs para scraping de servidores dedicados (AWS, Google Cloud, Hetzner). Para desenvolvimento e testes a partir de uma máquina local — autenticação user:pass.

Tratamento de erros e troca automática de IP

Mesmo com proxies de qualidade, erros ocorrerão: timeouts, recusas de conexão, bloqueios. O tratamento correto de erros é crítico para a operação estável do scraper.

Tratamento de status HTTP

class ProxyMiddleware:
    def process_response(self, request, response, spider):
        # Códigos para os quais precisamos trocar o proxy e repetir
        ban_codes = [403, 407, 429, 503]
        
        if response.status in ban_codes:
            proxy = request.meta.get('proxy')
            spider.logger.warning(
                f'Obtido {response.status} de {proxy}, tentando novamente...'
            )
            
            # Marcamos para retry com um novo proxy
            request.meta['dont_retry'] = False
            request.meta['proxy'] = self.get_new_proxy()
            
            return request
        
        return response

Tratamento de exceções de rede

from twisted.internet.error import TimeoutError, ConnectionRefusedError
from scrapy.exceptions import IgnoreRequest

class ProxyMiddleware:
    def process_exception(self, request, exception, spider):
        # Erros de conexão com o proxy
        proxy_errors = (
            TimeoutError,
            ConnectionRefusedError,
            ConnectionLost,
        )
        
        if isinstance(exception, proxy_errors):
            proxy = request.meta.get('proxy')
            spider.logger.error(
                f'Falha de conexão com o proxy {proxy}: {exception}'
            )
            
            # Mudamos o proxy e tentamos novamente
            request.meta['proxy'] = self.get_new_proxy()
            return request
        
        # Para outros erros, usamos o tratamento padrão
        return None

Detecção de bloqueios pelo conteúdo

Alguns sites retornam HTTP 200, mas mostram um captcha ou uma página de bloqueio:

class ProxyMiddleware:
    def process_response(self, request, response, spider):
        # Sinais de bloqueio no conteúdo
        ban_indicators = [
            'captcha',
            'acesso negado',
            'bloqueado',
            'tráfego incomum',
            'verificação de robô',
        ]
        
        body_text = response.text.lower()
        
        if any(indicator in body_text for indicator in ban_indicators):
            spider.logger.warning(
                f'Página de bloqueio detectada de {request.meta.get("proxy")}'
            )
            
            # Mudamos o proxy e repetimos
            request.meta['proxy'] = self.get_new_proxy()
            return request
        
        return response

Qual tipo de proxy escolher para Scrapy

A escolha do tipo de proxy depende do site-alvo, do orçamento e da velocidade de scraping necessária. Aqui está uma comparação das principais opções:

Tipo de proxy Velocidade Custo Quando usar
Proxy de data center Alta (50-200ms) Baixa ($1-3/IP) Sites simples sem proteção, APIs, ferramentas internas
Proxies residenciais Média (300-800ms) Média ($5-15/GB) E-commerce, redes sociais, sites com Cloudflare, geotargeting
Proxies móveis Baixa (500-1500ms) Alta ($50-150/IP) Aplicativos móveis, Instagram, TikTok, máxima proteção

Recomendações para escolha

Para scraping de marketplaces (Amazon, Wildberries, Ozon, AliExpress) — apenas proxies residenciais. Esses sites banem agressivamente data centers. É necessária rotação e geotargeting (por exemplo, IPs russos para Wildberries).

Para scraping de sites de notícias, blogs, fóruns — proxies de data center são adequados. A proteção é mínima, a velocidade e o baixo custo de tráfego são importantes.

Para scraping de sites com Cloudflare — proxies residenciais são obrigatórios. Data centers são detectados quase instantaneamente pelo Cloudflare. Adicione à Scrapy a biblioteca cloudscraper para contornar desafios de JS.

Para scraping do Google Search, ferramentas de SEO — proxies residenciais com geotargeting. O Google mostra resultados diferentes para diferentes países e cidades.

Dica: Comece com um pool de 10 proxies residenciais para testes. Se você receber bloqueios — aumente o pool para 50-100 IPs. Para scraping em alta velocidade (1000+ requisições/minuto), utilize um endpoint rotativo com um pool de 10.000+ IPs.

Técnicas avançadas: sessões e IPs fixos

Ao fazer scraping de alguns sites, é necessário manter um único IP durante toda a sessão (autenticação, carrinho de compras, formulários de múltiplas etapas). Veja como implementar sessões fixas no Scrapy.

IP fixo para um domínio

from urllib.parse import urlparse

class StickyProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        # Dicionário: domínio -> proxy
        self.domain_proxy_map = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Extraímos o domínio da URL
        domain = urlparse(request.url).netloc
        
        # Se já houver um proxy para este domínio — usamos
        if domain in self.domain_proxy_map:
            proxy = self.domain_proxy_map[domain]
        else:
            # Caso contrário, escolhemos um novo e lembramos
            proxy = random.choice(self.proxy_list)
            self.domain_proxy_map[domain] = proxy
            spider.logger.info(f'Atribuído {proxy} a {domain}')
        
        request.meta['proxy'] = proxy

IP fixo com timeout de sessão

Uma variante mais avançada: o proxy é vinculado ao domínio por um determinado tempo (por exemplo, 10 minutos), depois é trocado:

import time
from urllib.parse import urlparse

class SessionProxyMiddleware:
    def __init__(self, proxy_list, session_timeout=600):
        self.proxy_list = proxy_list
        self.session_timeout = session_timeout  # 10 minutos
        # Dicionário: domínio -> (proxy, tempo de criação)
        self.sessions = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        timeout = crawler.settings.getint('PROXY_SESSION_TIMEOUT', 600)
        return cls(proxy_list, timeout)
    
    def get_proxy_for_domain(self, domain):
        current_time = time.time()
        
        # Verificamos se há uma sessão ativa
        if domain in self.sessions:
            proxy, created_at = self.sessions[domain]
            
            # Se a sessão não expirou — usamos o mesmo proxy
            if current_time - created_at < self.session_timeout:
                return proxy
        
        # Criamos uma nova sessão com um novo proxy
        new_proxy = random.choice(self.proxy_list)
        self.sessions[domain] = (new_proxy, current_time)
        
        return new_proxy
    
    def process_request(self, request, spider):
        domain = urlparse(request.url).netloc
        proxy = self.get_proxy_for_domain(domain)
        request.meta['proxy'] = proxy

Integração com Cookie Middleware

Para sessões completas, é necessário sincronizar proxies e cookies. O Scrapy armazena cookies separadamente para cada domínio, mas ao trocar de proxy, é necessário limpar os cookies:

# settings.py

# Habilitamos o cookie middleware
COOKIES_ENABLED = True
COOKIES_DEBUG = False

# Middleware para sincronizar proxies e cookies
class ProxyCookieMiddleware:
    def process_request(self, request, spider):
        # Obtém o proxy atual
        current_proxy = request.meta.get('proxy')
        
        # Se o proxy mudou — limpamos os cookies
        previous_proxy = request.meta.get('previous_proxy')
        
        if previous_proxy and previous_proxy != current_proxy:
            # Limpamos os cookies para este domínio
            jar = spider.crawler.engine.downloader.middleware.middlewares[0].jars
            domain = urlparse(request.url).netloc
            
            if domain in jar:
                jar[domain].clear()
                spider.logger.info(f'Cookies limpos para {domain}')
        
        request.meta['previous_proxy'] = current_proxy

Conclusão

A configuração correta de proxies no Scrapy é a base para um scraping estável sem bloqueios. Nós cobrimos todos os aspectos-chave: desde a integração básica até técnicas avançadas de rotação e gerenciamento de sessões.

Principais conclusões:

  • Para produção, utilize um middleware personalizado com rotação inteligente e blacklist de IPs problemáticos
  • Trate todos os tipos de erros: status HTTP, exceções de rede, bloqueios por conteúdo
  • Escolha o tipo de proxy adequado para a tarefa: data centers para sites simples, residenciais para sites protegidos
  • Para sites com autenticação, utilize sessões fixas vinculando o proxy ao domínio
  • Comece com um pool de 10-50 proxies, escalando conforme o aumento da carga

Se você planeja fazer scraping de sites protegidos (marketplaces, redes sociais, sites com Cloudflare), recomendo usar proxies residenciais — eles oferecem máxima anonimidade e mínimo risco de bloqueios. Para scraping em alta velocidade, escolha provedores com endpoint rotativo e um pool de 10.000 endereços IP.

Todos os exemplos de código deste artigo foram testados no Scrapy 2.x e estão prontos para uso em produção. Adapte-os para suas necessidades e escale conforme o crescimento do projeto.

```