← Voltar ao blog

Proxies Assíncronos em httpx para Python: Rotação, HTTP/2 e 5 Erros Comuns de Iniciantes

Analisamos como configurar corretamente a rotação de proxies no httpx, ativar HTTP/2 e evitar os erros comuns que 90% dos desenvolvedores iniciantes de parsers cometem.

📅17 de outubro de 2026

A biblioteca httpx se tornou o padrão para requisições HTTP assíncronas em Python, mas ao trabalhar com proxies, existem nuances que podem quebrar parsers em produção. Vamos analisar como configurar a rotação assíncrona de proxies, habilitar o HTTP/2 e por que seu script falha a cada 50-100 requisições.

Por que httpx e não requests ou aiohttp

requests é síncrono e não suporta HTTP/2 nativamente. aiohttp é assíncrono, mas a configuração de proxies lá é menos amigável, e o suporte ao HTTP/2 está completamente ausente. httpx resolve ambas as questões: um cliente síncrono e assíncrono com uma API unificada, suporte nativo ao HTTP/2 através da extensão h2, e uma operação flexível com proxies no nível de transporte.

Para tarefas de parsing de marketplaces (Wildberries, Ozon, Avito), coleta de dados de APIs de redes sociais ou monitoramento de preços de concorrentes, a assíncrona é crítica — ela proporciona um aumento significativo de velocidade em comparação com requisições sequenciais. Mas sem uma rotação de proxies adequada e gerenciamento de conexões, a assíncrona se torna uma fonte de novos problemas: exaustão do pool de conexões, corrotinas travadas, vazamentos de descritores de arquivos.

Um segundo ponto importante — o HTTP/2 é capaz de multiplexar várias requisições em uma única conexão TCP, o que reduz a carga no servidor de destino e ajuda a contornar alguns sistemas anti-bot ingênuos que observam a quantidade de handshakes TCP de um único IP.

Configuração básica de proxies em httpx

A partir da versão httpx 0.26+, a sintaxe para configuração de proxies mudou — em vez de um dicionário proxies, agora se usa o parâmetro proxy. Isso é uma fonte comum de confusão se você estiver assistindo a tutoriais antigos.

import httpx
import asyncio

PROXY_URL = "http://user:[email protected]:8000"

async def fetch(url: str):
    async with httpx.AsyncClient(proxy=PROXY_URL, timeout=15.0) as client:
        response = await client.get(url)
        return response.status_code, response.text[:200]

asyncio.run(fetch("https://httpbin.org/ip"))

Se você precisar separar proxies para tráfego HTTP e HTTPS ou configurar proxies diferentes para diferentes domínios, use httpx.Client(mounts=...) — transportes são montados com base no padrão de URL:

mounts = {
    "http://": httpx.AsyncHTTPTransport(proxy="http://user:[email protected]:8000"),
    "https://": httpx.AsyncHTTPTransport(proxy="http://user:[email protected]:8000"),
}

async with httpx.AsyncClient(mounts=mounts) as client:
    response = await client.get("https://example.com")

Para autenticação com login e senha, o formato http://user:pass@host:port funciona de forma estável para proxies HTTP. Para SOCKS5, você precisará instalar httpx[socks] adicionalmente, caso contrário, você receberá um ImportError em tempo de execução, e não na fase de escrita do código — essa é uma armadilha na qual muitos caem.

Rotação assíncrona de proxies: código funcional

O principal erro na rotação de proxies é criar um novo AsyncClient para cada requisição. Isso mata a performance, pois o cliente mantém um pool de conexões, e sua recriação anula todas as vantagens das conexões keep-alive. A abordagem correta é criar um pool de clientes antecipadamente, um para cada proxy, e distribuir as tarefas entre eles.

import httpx
import asyncio
import itertools
import random

PROXIES = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]

class ProxyPool:
    def __init__(self, proxies: list[str]):
        self.clients = [
            httpx.AsyncClient(proxy=p, timeout=httpx.Timeout(15.0, connect=5.0))
            for p in proxies
        ]
        self._cycle = itertools.cycle(self.clients)

    def get_client(self) -> httpx.AsyncClient:
        return next(self._cycle)  # round-robin
        # para seleção aleatória: random.choice(self.clients)

    async def close_all(self):
        for client in self.clients:
            await client.aclose()

async def fetch_with_retry(pool: ProxyPool, url: str, retries: int = 3):
    for attempt in range(retries):
        client = pool.get_client()
        try:
            response = await client.get(url)
            if response.status_code == 200:
                return response.text
        except (httpx.ConnectTimeout, httpx.ProxyError, httpx.ReadTimeout):
            await asyncio.sleep(0.5 * (attempt + 1))
            continue
    return None

async def main():
    pool = ProxyPool(PROXIES)
    urls = [f"https://example.com/page/{i}" for i in range(100)]

    sem = asyncio.Semaphore(20)  # limitando o paralelismo

    async def bound_fetch(url):
        async with sem:
            return await fetch_with_retry(pool, url)

    results = await asyncio.gather(*(bound_fetch(u) for u in urls))
    await pool.close_all()
    return results

asyncio.run(main())

Este padrão resolve três problemas de uma vez: reutiliza conexões TCP através de keep-alive, distribui a carga entre proxies de forma round-robin e limita o paralelismo através de um semáforo, para não sobrecarregar o servidor de destino e não provocar bloqueios por rate-limit.

Para parsers em produção que trabalham com Wildberries, Ozon ou Instagram API, é importante que o pool de proxies seja grande o suficiente e diversificado geograficamente — caso contrário, a rotação dentro de uma mesma sub-rede não salvará você de bloqueios. Aqui, proxies residenciais são uma boa escolha, pois possuem IPs de usuários reais e raramente caem em filtros de sistemas anti-bot que analisam ASN e a reputação do intervalo.

HTTP/2 em httpx: por que e como habilitar

Por padrão, httpx utiliza HTTP/1.1. Para habilitar o HTTP/2, você precisa instalar a extensão e passar o flag explicitamente:

# pip install httpx[http2]

async with httpx.AsyncClient(http2=True, proxy=PROXY_URL) as client:
    response = await client.get("https://example.com")
    print(response.http_version)  # esperamos "HTTP/2"

Um detalhe importante: nem todos os proxies suportam o tunelamento HTTP/2 corretamente. Parte dos proxies de data centers baratos funciona apenas como proxies HTTP/1.1, mesmo que o servidor final suporte HTTP/2 — nesse caso, httpx silenciosamente reverterá para HTTP/1.1 através de um túnel CONNECT, e você nem sempre notará isso sem uma verificação explícita de response.http_version.

O HTTP/2 oferece um ganho real em três cenários: quando você faz muitas requisições a um único domínio (o multiplexamento em uma única conexão reduz a latência), quando é importante reduzir a visibilidade das requisições para a proteção anti-bot (menos handshakes TCP de um único IP parece mais natural), e quando o site de destino exige HTTP/2 e retorna erros ou conteúdo reduzido para clientes em HTTP/1.1 — isso às vezes acontece em sites na Cloudflare.

Para tarefas onde a velocidade e a estabilidade das conexões HTTP/2 através de proxies são importantes, proxies dedicados de data centers são mais adequados — eles são mais rápidos que os residenciais e geralmente suportam corretamente o tunelamento de protocolos modernos.

5 erros típicos com proxies em httpx

1. Recriação do cliente para cada requisição

Código do tipo async with httpx.AsyncClient(...) as client: await client.get(url) dentro de um loop — isso é um antipadrão. Cada criação de cliente resulta em uma nova conexão TCP, um novo handshake TLS, ignorando completamente o pooling de conexões. Em 1000 requisições, a diferença de velocidade entre a abordagem correta e a incorreta pode chegar a 5-10 vezes.

2. Ausência de timeouts ou timeouts muito longos por padrão

Por padrão, httpx define um timeout de 5 segundos para tudo de uma vez — connect, read, write, pool. Ao trabalhar com proxies instáveis, isso é insuficiente para connect, mas excessivo para read, se o proxy simplesmente "trava". Separe os timeouts explicitamente:

timeout = httpx.Timeout(
    connect=5.0,
    read=10.0,
    write=10.0,
    pool=5.0
)
client = httpx.AsyncClient(proxy=PROXY_URL, timeout=timeout)

3. Ignorando os limites do pool de conexões

Com um grande número de tarefas paralelas (gather em 500+ corrotinas sem semáforo), httpx começa a lançar PoolTimeout, porque o limite de conexões no pool (por padrão 100) foi atingido. Configure os limites explicitamente através de httpx.Limits(max_connections=200, max_keepalive_connections=50) e sempre use um semáforo para controlar o paralelismo.

4. Desativando a verificação sem entender as consequências

Muitos copiam verify=False do Stack Overflow para se livrar de erros SSL ao trabalhar através de proxies com certificados MITM. Isso abre a porta para ataques "man-in-the-middle" e pode levar a vazamentos de dados através de proxies comprometidos. Se o proxy exigir um certificado especial — adicione-o explicitamente através de verify="/path/to/cert.pem", em vez de desativar a verificação completamente.

5. Clientes não fechados e vazamento de descritores de arquivos

Se você criar um pool de clientes manualmente (como no exemplo acima), não se esqueça de chamar await client.aclose() para cada um deles ao finalizar o trabalho. Scripts que rodam por longos períodos sem um fechamento explícito das conexões esbarram no limite de descritores de arquivos abertos do sistema operacional (geralmente 1024 no Linux por padrão), e o parser falha com um erro enigmático Too many open files após algumas horas de operação.

Qual tipo de proxy escolher para parsing

A escolha do proxy afeta diretamente quantos erros da lista acima você verá — proxies instáveis provocam timeouts e desconexões, mesmo que o código esteja escrito perfeitamente.

Tipo de Proxy Velocidade Risco de Bloqueio Quando Usar
Data Center Alta Alta em sites protegidos API aberta, proteção anti-bot fraca
Residencial Média Baixo Wildberries, Ozon, redes sociais
Móvel Média-baixa Mínimo Instagram, TikTok API, proteção rigorosa

Para parsing assíncrono com alta velocidade de requisições a APIs com proteção moderada, proxies de data center continuam sendo uma opção economicamente viável — o principal é distribuir uniformemente a carga pelo pool, como mostrado no código acima.

Checklist antes da produção

  • Clientes httpx são criados uma vez e reutilizados, e não a cada requisição
  • Timeouts são separados em connect/read/write/pool, e não se usa o padrão
  • Paralelismo é limitado através de asyncio.Semaphore
  • Limites do pool de conexões são configurados para a carga real
  • Há lógica de retry com tratamento de ProxyError, ConnectTimeout, ReadTimeout
  • Todos os clientes são fechados explicitamente através de aclose() ao finalizar
  • verify=False não é usado sem extrema necessidade
  • HTTP/2 é habilitado conscientemente e verificado através de response.http_version

Conclusão

httpx oferece um poderoso conjunto de ferramentas para trabalho assíncrono com proxies e HTTP/2, mas requer uma configuração cuidadosa do pool de clientes, timeouts e limites de conexões. A maioria das falhas de parsers em produção está relacionada não à biblioteca, mas a erros arquiteturais: recriação de clientes, falta de limitação de paralelismo e tratamento inadequado de exceções de proxies.

Se você está construindo um parser para marketplaces ou APIs de redes sociais que monitoram ativamente atividades suspeitas, a estabilidade da infraestrutura de proxies é tão importante quanto o código. Para tais tarefas, vale a pena considerar proxies residenciais — eles reduzem a taxa de erros ProxyError e ConnectTimeout devido à maior reputação dos endereços IP em comparação com pools de data center baratos.