Rotação de Proxy no Crawlee para Python: Guia de ProxyConfiguration e SessionPool

Aprenda a implementar rotação de proxy no Crawlee para Python usando ProxyConfiguration, SessionPool e proxies residenciais. Exemplos executáveis, padrões de produção e armadilhas reais.

Proxy Rotation in Crawlee for Python: A Developer's Guide to Residential IPs
Neste artigo

Aviso legal: Este guia aborda coleta de dados públicos apenas. Sempre respeite o robots.txt, os Termos de Serviço dos sites, e legislações aplicáveis como o CFAA nos EUA e o GDPR na UE. Prefira APIs oficiais quando disponíveis. O uso de proxies para contornar bloqueios deve ser ético e proporcional.

Se você está construindo crawlers em Python com o Crawlee e seus requests começam a retornar 403 Forbidden após as primeiras 50 requisições, você não está sozinho. A rotação de proxy no Crawlee para Python é a diferença entre um crawler que coleta milhares de páginas e um que morre após minutos de execução. Este guia mostra como integrar proxies residenciais de forma idiomática usando ProxyConfiguration e SessionPool, com exemplos executáveis e padrões de produção.

Como funciona a rotação de proxy no Crawlee para Python

O Crawlee para Python (o port da stack Apify Crawlee) oferece uma arquitetura onde BeautifulSoupCrawler e PlaywrightCrawler operam sobre uma fila de requests unificada, com autoscaling automático e um SessionPool que vincula cookies, headers e fingerprints a IPs específicos. A classe ProxyConfiguration é o ponto de extensão idiomático para gerenciar proxies — ela gera URLs de proxy por sessão ou por requisição, e se integra diretamente ao SessionPool.

Quando você passa um ProxyConfiguration para o construtor do crawler, o framework automaticamente:

  • Atribui um proxy a cada sessão no SessionPool;
  • Mantém o mesmo IP para todas as requisições dentro de uma sessão (sticky session);
  • Rotaciona para um novo IP quando a sessão é retirada via session.retire();
  • Repassa o proxy configurado para o BeautifulSoupCrawler (via httpx) ou para o PlaywrightCrawler (via o contexto do browser).

Isso significa que a rotação de proxy não é um hack externo — é parte do ciclo de vida de sessões do framework.

Arquitetura do Crawlee: BeautifulSoupCrawler, PlaywrightCrawler e SessionPool

Antes de mergulhar na configuração de proxy, é importante entender como o Crawlee estrutura o processamento de requests.

BeautifulSoupCrawler vs PlaywrightCrawler

O BeautifulSoupCrawler usa httpx para fazer requisições HTTP e BeautifulSoup para parsing. É rápido — tipicamente 200-500ms por request — e ideal para sites estáticos ou que retornem HTML completo sem necessidade de execução de JavaScript.

O PlaywrightCrawler usa um browser headless (Chromium, Firefox, ou WebKit) via Playwright. É mais lento — 2-5 segundos por page load — mas necessário para SPAs (Single Page Applications) que renderizam conteúdo via JavaScript.

SessionPool: cookies, fingerprints e IPs

O SessionPool é o componente que torna a rotação de proxy eficaz. Cada sessão no pool carrega:

  • Cookies acumulados durante a navegação;
  • Headers HTTP consistentes (User-Agent, Accept, etc.);
  • Um proxy vinculado — se você configurou ProxyConfiguration, a sessão mantém o mesmo IP até ser retirada.

Isso é crítico: se você rotaciona IPs a cada request mas mantém os mesmos cookies, o site-alvo detecta a inconsistência (cookies de sessão A vindos de IP B) e bloqueia. O Crawlee resolve isso amarrando cookies ao IP dentro de cada sessão.

Autoscaling e concorrência

O Crawlee usa um AutoscaledPool que ajusta a concorrência dinamicamente com base na taxa de sucesso e na latência das requisições. Se a taxa de erro sobe, o pool reduz a concorrência automaticamente. Isso significa que proxies ruins não apenas geram erros — eles também reduzem o throughput do crawler inteiro.

ProxyConfiguration: a API idiomática do Crawlee

A classe ProxyConfiguration é o ponto de extensão correto para gerenciar proxies no Crawlee. Ela expõe o método new_url(), que retorna uma URL de proxy formatada para uma sessão específica.

Rotação round-robin vs sessões sticky

Existem duas estratégias principais:

Round-robin — cada chamada a new_url() sem session_id retorna um proxy diferente:

from crawlee.proxy_configuration import ProxyConfiguration

proxy_config = ProxyConfiguration(proxy_urls=[
    'http://user:pass@gate.proxyhat.com:8080',
    'http://user:pass@gate.proxyhat.com:8080',
    'http://user:pass@gate.proxyhat.com:8080',
])

# Cada chamada retorna um proxy diferente
url1 = await proxy_config.new_url()
url2 = await proxy_config.new_url()

Sticky session — passe um session_id e o mesmo IP é mantido para aquela sessão:

# Mesmo session_id = mesmo IP
url = await proxy_config.new_url(session_id='abc123')

Para a maioria dos casos de produção, sessões sticky são superiores. Sites de e-commerce, redes sociais e plataformas de tickets vinculam carrinhos, sessões de login e tokens CSRF a IPs. Rotacionar IP a cada request quebra essas sessões.

Integrando ProxyConfiguration com o SessionPool

O Crawlee conecta automaticamente o ProxyConfiguration ao SessionPool. Quando você cria um crawler com proxy_configuration, o framework:

  1. Cria uma sessão no pool;
  2. Chama proxy_configuration.new_url(session_id=session.id);
  3. Vincula o proxy retornado à sessão;
  4. Usa esse proxy para todas as requisições da sessão;
  5. Quando session.retire() é chamado, a sessão é descartada e uma nova é criada com um novo proxy.

Isso é o padrão idiomático — não há necessidade de gerenciar proxies manualmente no request_handler.

Por que proxies residenciais superam datacenter em sites com anti-bot

Sites protegidos por Cloudflare, DataDome, PerimeterX e Akamai Bot Manager usam listas de IPs de datacenter conhecidas. O Cloudflare mantém um banco de dados de ranges ASN associados a provedores de hospedagem (AWS, GCP, DigitalOcean, etc.) e aplica desafios automaticamente a esses IPs.

Proxies residenciais usam IPs de ISPs reais (Comcast, AT&T, Vodafone, Telefonica), tornando o tráfego indistinguível de visitantes humanos. A diferença é dramática:

Métrica Datacenter Proxy Residencial Proxy
Taxa de bloqueio (Cloudflare) 60-90% 5-15%
Latência média 50-200ms 300-800ms
Custo por GB $0.5-$2 $3-$15
Concorrência recomendada 50-100 10-30

Estratégia de proxy em camadas (tiered fallback)

Para maximizar custo-eficiência, muitos crawlers de produção usam uma estratégia em camadas:

  1. Camada 1 — Datacenter: tenta primeiro; se receber 403, retira a sessão;
  2. Camada 2 — Residencial: faz retry com proxy residencial;
  3. Camada 3 — Mobile: último recurso para sites extremamente protegidos.

No Crawlee, isso pode ser implementado com múltiplas ProxyConfiguration instâncias e lógica de fallback no request_handler.

Exemplo executável: BeautifulSoupCrawler com ProxyConfiguration e ProxyHat

Aqui está um exemplo completo de um crawler que usa proxies residenciais do ProxyHat com sessões sticky por país:

import asyncio
import hashlib
from crawlee.beautifulsoup_crawler import BeautifulSoupCrawler
from crawlee.beautifulsoup_crawler import BeautifulSoupCrawlingContext
from crawlee.proxy_configuration import ProxyConfiguration
from crawlee.sessions import SessionPool

PROXYHAT_GATEWAY = 'gate.proxyhat.com'
PROXYHAT_PORT = 8080
PROXYHAT_USER = 'seu_usuario'
PROXYHAT_PASS = 'sua_senha'

def build_proxy_url(session_id: str, country: str = 'US') -> str:
    """Gera uma URL de proxy ProxyHat com país e sessão fixos."""
    username = f'{PROXYHAT_USER}-country-{country}-sid-{session_id}'
    return f'http://{username}:{PROXYHAT_PASS}@{PROXYHAT_GATEWAY}:{PROXYHAT_PORT}'

class ProxyHatConfiguration(ProxyConfiguration):
    """ProxyConfiguration customizada que gera URLs ProxyHat por sessão."""

    async def new_url(self, session_id: str | None = None) -> str:
        if session_id is None:
            # Sem session_id = round-robin (sem flag de sessão)
            username = f'{PROXYHAT_USER}-country-US'
            return f'http://{username}:{PROXYHAT_PASS}@{PROXYHAT_GATEWAY}:{PROXYHAT_PORT}'
        # Com session_id = sticky session
        sid = hashlib.md5(session_id.encode()).hexdigest()[:12]
        return build_proxy_url(sid, country='US')

async def main():
    proxy_config = ProxyHatConfiguration()

    crawler = BeautifulSoupCrawler(
        proxy_configuration=proxy_config,
        max_request_retries=3,
        max_requests_per_crawl=500,
        request_handler_timeout=30,
        session_pool=SessionPool(
            max_pool_size=20,
            create_session_timeout=10,
        ),
    )

    @crawler.router.default_handler
    async def handler(context: BeautifulSoupCrawlingContext) -> None:
        status = context.http_response.status_code
        context.log.info(
            f'Status {status} | URL: {context.request.url} '
            f'| Session: {context.session.id if context.session else "none"}'
        )

        if status == 403 or status == 429:
            # Bloqueio detectado: retira a sessão e seu IP vinculado
            if context.session:
                context.session.retire()
            context.request.retry_count += 1
            raise RuntimeError(f'Bloqueado: {status}')

        # Parsing normal
        title = context.soup.find('title')
        if title:
            await context.push_data({
                'url': context.request.url,
                'title': title.text.strip(),
            })

        # Extrai links e enfileira
        for link in context.soup.find_all('a', href=True):
            await context.enqueue_links(
                selector='a[href]',
                label='LINK',
            )

    await crawler.run(['https://example.com'])

if __name__ == '__main__':
    asyncio.run(main())

Observe os pontos-chave:

  • O ProxyHatConfiguration herda de ProxyConfiguration e sobrescreve new_url() — essa é a extensão idiomática do framework;
  • O session_id é hasheado para criar um identificador estável no ProxyHat;
  • A flag -country-US-sid-abc123 no username instrui o gateway do ProxyHat a fixar o IP residencial para aquela sessão;
  • O SessionPool com max_pool_size=20 mantém até 20 IPs residenciais simultâneos;
  • O session.retire() descarta a sessão e seu IP, forçando o Crawlee a criar uma nova com um novo proxy.

Usando curl para testar o gateway

Antes de rodar o crawler, valide a conexão com curl:

curl -x http://seu_usuario-country-US-sid-test123:sua_senha@gate.proxyhat.com:8080 \
  https://httpbin.org/ip

Repita a chamada com o mesmo sid-test123 e você verá o mesmo IP. Mude o session ID e receberá um IP diferente.

Exemplo com PlaywrightCrawler e SOCKS5

Para sites que exigem rendering JavaScript, o PlaywrightCrawler é necessário. O ProxyHat também suporta SOCKS5:

from crawlee.playwright_crawler import PlaywrightCrawler
from crawlee.playwright_crawler import PlaywrightCrawlingContext
from crawlee.proxy_configuration import ProxyConfiguration

PROXYHAT_USER = 'seu_usuario'
PROXYHAT_PASS = 'sua_senha'

class ProxyHatSocks5Configuration(ProxyConfiguration):
    async def new_url(self, session_id: str | None = None) -> str:
        sid = session_id or 'default'
        username = f'{PROXYHAT_USER}-country-US-sid-{sid}'
        return f'socks5://{username}:{PROXYHAT_PASS}@gate.proxyhat.com:1080'

async def main():
    proxy_config = ProxyHatSocks5Configuration()

    crawler = PlaywrightCrawler(
        proxy_configuration=proxy_config,
        max_request_retries=2,
        browser_type='chromium',
        headless=True,
    )

    @crawler.router.default_handler
    async def handler(context: PlaywrightCrawlingContext) -> None:
        page = context.page
        await page.wait_for_load_state('networkidle')

        # Detecta CAPTCHA do Cloudflare
        if await page.query_selector('#challenge-running'):
            if context.session:
                context.session.retire()
            raise RuntimeError('Cloudflare challenge detectado')

        title = await page.title()
        await context.push_data({'url': context.request.url, 'title': title})

    await crawler.run(['https://example.com'])

if __name__ == '__main__':
    import asyncio
    asyncio.run(main())

Padrões de produção: retry, concorrência e tratamento de erros

session.retire() no bloqueio

Quando um site retorna 403, 429, ou redireciona para uma página de CAPTCHA, você deve chamar session.retire(). Isso instrui o Crawlee a:

  1. Descartar a sessão atual (cookies, headers, IP);
  2. Criar uma nova sessão com um novo proxy do ProxyConfiguration;
  3. Fazer retry do request com a nova sessão (se retry_count < max_request_retries).

Não use session.retire() para erros transitórios como timeouts de rede ou 500s do servidor — nesses casos, apenas deixe o retry normal acontecer.

max_request_retries e backoff

O parâmetro max_request_retries controla quantas vezes um request é retentado antes de ser marcado como falha. Para crawlers com proxies residenciais:

  • max_request_retries=3 é um bom padrão;
  • Cada retry usa uma nova sessão (se a anterior foi retirada);
  • O Crawlee aplica backoff exponencial automaticamente entre retries.

Concorrência via AutoscaledPool

O AutoscaledPool do Crawlee ajusta a concorrência com base na taxa de erro. Para proxies residenciais, recomendamos:

  • min_concurrency=5 — evita overhead de warm-up;
  • max_concurrency=20 — proxies residenciais têm latência maior;
  • max_tasks_per_minute=120 — limita a taxa global.

Configurar concorrência muito alta com proxies residenciais gera mais bloqueios, não mais throughput.

Tratamento de erros no request_handler

@crawler.router.default_handler
async def handler(context: BeautifulSoupCrawlingContext) -> None:
    try:
        status = context.http_response.status_code
        
        if status in (403, 429):
            if context.session:
                context.session.retire()
            raise RuntimeError(f'Bloqueado: {status}')
        
        if status >= 500:
            # Erro do servidor: retry sem retirar sessão
            raise RuntimeError(f'Erro do servidor: {status}')
        
        # Processamento normal
        data = extract_data(context.soup)
        await context.push_data(data)
        
    except Exception as e:
        context.log.error(f'Erro em {context.request.url}: {e}')
        raise  # Re-raise para acionar retry do Crawlee

Quando NÃO usar browser crawling

Existe uma tendência a usar PlaywrightCrawler por padrão, mas isso é um erro comum. Browser crawling é 5-10x mais lento e consome muito mais recursos. Use PlaywrightCrawler apenas quando:

  • O site é uma SPA que renderiza conteúdo via JavaScript (React, Vue, Angular);
  • O site requer interação (cliques, scrolls, preenchimento de formulários);
  • O site detecta requests sem execução de JavaScript.

Para 80% dos casos de scraping de dados públicos — SERP, e-commerce, blogs, fóruns — BeautifulSoupCrawler com proxies residenciais é suficiente e significativamente mais eficiente.

Considerações éticas e legais

Antes de qualquer implementação, considere:

  • robots.txt: o Crawlee respeita robots.txt por padrão. Não desabilite isso sem motivo justificado;
  • Termos de Serviço: muitos sites proíbem scraping automático em seus ToS. Violar ToS pode ter consequências legais;
  • CFAA (EUA): o Computer Fraud and Abuse Act pode aplicar-se a acesso não autorizado a sistemas computacionais. Consulte um advogado se o alvo está nos EUA;
  • GDPR (UE): dados pessoais coletados via scraping estão sujeitos ao GDPR. Não armazene PII sem base legal;
  • Rate limits: mesmo com proxies, respeite limites razoáveis. 1-2 requests por segundo por domínio é um bom padrão;
  • APIs oficiais: se o site oferece uma API, use-a. É mais estável, legal e frequentemente mais rápida.

ProxyHat: configuração e integração

O ProxyHat oferece proxies residenciais, mobile e datacenter com gateway unificado em gate.proxyhat.com. A integração com o Crawlee é direta via ProxyConfiguration.

Parâmetros do gateway

Parâmetro Valor
Gateway gate.proxyhat.com
Porta HTTP 8080
Porta SOCKS5 1080
Geo-targeting -country-US, -country-DE-region-state_of_berlin-city-berlin
Sticky session -sid-abc123

Consulte nossa documentação oficial para detalhes completos de autenticação e features. Para ver preços e planos, visite a página de pricing. Confira também nossos locations disponíveis em mais de 150 países.

Casos de uso

Os padrões deste guia aplicam-se diretamente a:

  • Web scraping de e-commerce para price monitoring;
  • SERP tracking para SEO e análise de concorrência;
  • Coleta de dados públicos para treinamento de modelos de IA;
  • QA automatizado de aplicações web em múltiplas geolocalizações.

Erros comuns e armadilhas

1. Rotacionar IP sem rotacionar sessão

Se você troca o proxy mas mantém a mesma sessão (cookies, User-Agent), o site detecta a inconsistência. Sempre use session.retire() ao trocar de proxy.

2. Concorrência muito alta com proxies residenciais

Proxies residenciais têm latência de 300-800ms. Configurar max_concurrency=100 gera timeouts e erros, não mais throughput. Mantenha entre 10-30.

3. Ignorar headers HTTP

O Crawlee envia headers padrão. Se todos os seus requests têm o mesmo User-Agent, o site detecta automação. Use httpx headers customizados ou o playwright_fingerprint_generator para variar.

4. Não monitorar a taxa de sucesso

Se sua taxa de sucesso cai abaixo de 90%, algo está errado — proxies ruins, concorrência muito alta, ou detecção anti-bot. Monitore via Statistics do Crawlee.

5. Usar datacenter proxies para sites protegidos

Cloudflare e DataDome bloqueiam IPs de datacenter em segundos. Não perca tempo tentando — vá direto para residenciais.

Perguntas frequentes

Por que a rotação de proxy no Crawlee para Python importa para usuários de proxy?

Porque sites com proteção anti-bot como Cloudflare e DataDome detectam e bloqueiam IPs de datacenter rapidamente. A rotação de proxy no Crawlee, combinada com proxies residenciais e sessões sticky, permite que cada sessão mantenha um IP residencial consistente, preservando cookies e fingerprints legítimos, o que reduz drasticamente a taxa de bloqueio em comparação com IPs de datacenter.

Como evitar bloqueios ao implementar rotação de proxy no Crawlee para Python?

Use proxies residenciais com sessões sticky via ProxyConfiguration.new_url(session_id=...), chame session.retire() ao detectar bloqueios (status 403, CAPTCHAs), configure max_request_retries adequadamente, respeite robots.txt, limite a concorrência com o autoscaled pool, e implemente fallback entre tipos de proxy. Também monitore a taxa de sucesso e ajuste a concorrência dinamicamente.

Pronto para começar?

Proxies residenciais, ISP e móveis em mais de 195 países. Crie uma conta grátis.

Criar conta grátis
← Voltar ao Blog