Guia de Proxy curl_cffi: Configuração, Autenticação, Rotação e Assíncrono

curl_cffi oferece um handshake TLS com formato de navegador. Um proxy fornece um IP de saída limpo. Aqui está exatamente como conectar os dois — e por que o prefixo https:// no seu dicionário de proxies está gerando ErrCode 35.

curl_cffi é a ligação Python para um fork do curl-impersonate: ele reproduz as impressões digitais TLS/JA3 e HTTP/2 de um navegador real em vez de se anunciar como urllib3. Isso corrige um eixo de bloqueio. O outro é o IP de saída, que é onde entra um proxy curl_cffi — e onde a documentação é escassa. A seção oficial de proxy tem cerca de quinze linhas, e uma issue no GitHub de fevereiro de 2023 ainda está entre as cinco principais para este tópico. Este guia cobre toda a superfície: o parâmetro proxy, o dicionário no estilo requests e seus nomes de chave reais, proxy_auth, sessões, rotação por solicitação, assíncrono, SOCKS5 — e as exatas mensagens de erro que você irá colar em uma caixa de pesquisa.

Sintaxe de proxy curl_cffi: prefira proxy= em vez do dicionário proxies

curl_cffi aceita duas formas. A nativa é uma única string proxy=, adicionada na v0.6.0; o dicionário proxies= existe para compatibilidade com requests, e a documentação recomenda o parâmetro único a menos que você realmente precise de proxies diferentes por esquema. Internamente, eles se colapsam na mesma coisa — proxy="..." torna-se {"all": "..."} — e ambos funcionam nos auxiliares do módulo, em Session, em AsyncSession e em solicitações individuais.

# pip install curl_cffi --upgrade    (Python 3.10+ since v0.14)
import curl_cffi

PROXY = "http://USER:PASS@gate.quantumproxies.io:PORT"

# Native form — one string, applies to every scheme
r = curl_cffi.get(
    "https://tls.browserleaks.com/json",
    impersonate="chrome",
    proxy=PROXY,
    timeout=30,
)
print(r.status_code, r.json()["ja3n_hash"])

# requests-compatible form
r = curl_cffi.get(
    "https://httpbin.org/ip",
    impersonate="chrome",
    proxies={"http": PROXY, "https": PROXY},
    timeout=30,
)
print(r.json())  # {'origin': '<proxy exit IP>'}

Quatro coisas sobre esse dicionário valem a pena saber, porque nenhuma delas é óbvia a partir do README:

Uma nota sobre importações: desde a v0.10.0 o pacote é chamável diretamente (curl_cffi.get, curl_cffi.Session). Tutoriais mais antigos usam from curl_cffi import requests, o que ainda funciona, mas parece ruim ao lado da biblioteca requests real — e explica por que metade dos trechos online parecem um projeto diferente.

A armadilha https://: ErrCode 35 e WRONG_VERSION_NUMBER

Este único erro gera mais perguntas sobre proxy curl_cffi do que tudo o mais combinado. A issue #6 no rastreador do projeto — aberta e fechada no mesmo dia em fevereiro de 2023 — ainda está na página um, porque o erro que produz parece um bug de TLS em vez de um erro de configuração:

# WRONG: this asks curl to open a TLS connection *to the proxy itself*
proxies = {"https": "https://USER:PASS@gate.quantumproxies.io:PORT"}

# Failed to perform, ErrCode: 35, Reason:
# 'error:100000f7:SSL routines:OPENSSL_internal:WRONG_VERSION_NUMBER'

# RIGHT: plain HTTP CONNECT, then the TLS tunnel runs through to the target
proxies = {"https": "http://USER:PASS@gate.quantumproxies.io:PORT"}

# Or skip the dict entirely
proxy = "http://USER:PASS@gate.quantumproxies.io:PORT"

A chave nomeia o protocolo do alvo; o valor nomeia como você alcança o proxy. Um proxy HTTPS-over-HTTP normal aceita um CONNECT em texto simples, depois canaliza seu tráfego criptografado sem alterações — então a URL do proxy começa com http:// mesmo quando todas as URLs que você busca são HTTPS. Proxies HTTPS-over-HTTPS existem, mas são raros e devem ser explicitamente suportados pelo gateway. Requests expressa a mesma falha de forma muito mais útil — seu proxy parece usar apenas HTTP e não HTTPS — o que é por isso que uma configuração idêntica pode parecer um bug específico do curl_cffi. Versões recentes avisam e vinculam a issue #6, mas é apenas um aviso: a solicitação ainda falha.

Autenticação: credenciais na URL ou proxy_auth

Gateways autenticados aceitam a forma embutida usual, http://USER:PASS@host:port, com a ressalva usual: um @, : ou / não escapado na senha divide a URL no lugar errado e produz uma falha de autenticação que parece um proxy inativo. curl_cffi oferece uma saída que requests não oferece — uma tupla proxy_auth passada para libcurl como opções de nome de usuário e senha separadas, então nenhuma codificação está envolvida.

import curl_cffi
from urllib.parse import quote

# Option A — credentials in the URL, password URL-encoded
pw = quote("p@ss:word", safe="")
r = curl_cffi.get(
    "https://httpbin.org/ip",
    proxy=f"http://USER:{pw}@gate.quantumproxies.io:PORT",
    impersonate="chrome",
    timeout=30,
)

# Option B — keep credentials out of the URL entirely
r = curl_cffi.get(
    "https://httpbin.org/ip",
    proxy="http://gate.quantumproxies.io:PORT",
    proxy_auth=("USER", "PASS"),
    impersonate="chrome",
    timeout=30,
)
print(r.json())

Uma terceira opção elimina completamente essa classe de bug: lista branca de IP. Todo plano residencial QuantumProxies permite que você autorize o IP do seu servidor em vez de enviar user:pass, então a URL do proxy se torna um simples http://gate.quantumproxies.io:PORT — nada para codificar, nenhum segredo na sua árvore de código-fonte. Se as próprias credenciais estão sendo rejeitadas, nosso guia para todas as causas de 407 Proxy Authentication Required cobre o restante.

Diagrama de fluxo de uma solicitação curl_cffi passando por um gateway de proxy rotativo até o site de destino com um handshake TLS compatível com o navegador
O proxy canaliza o handshake em vez de terminá-lo, então a personificação do navegador sobrevive ao salto e o destino vê um IP de saída limpo.

Sessões, cookies e o detalhe de reutilização de credenciais

Uma Session mantém cookies, agrupamento de conexões e seus padrões em um só lugar, que é o que você quer para qualquer coisa em várias etapas. Defina impersonate e proxy uma vez e toda solicitação os herda:

from curl_cffi import Session

with Session(
    impersonate="chrome",
    proxy="http://USER-session-a1b2:PASS@gate.quantumproxies.io:PORT",
    timeout=30,
    retry=3,
) as s:
    s.get("https://httpbin.org/cookies/set/foo/bar")
    r = s.get("https://httpbin.org/cookies")
    print(r.json(), s.cookies.get_dict())

Dois comportamentos merecem destaque. Primeiro, sempre que um proxy é configurado, curl_cffi ativa a opção proxy-credential-no-reuse do libcurl: uma nova conexão é forçada quando o nome de usuário do proxy muda, e o cache de sessão TLS é baseado no endereço do proxy, então um IP de saída anterior não pode vazar em uma solicitação posterior através de uma sessão reutilizada. Se você codificar IDs de sessão persistente no nome de usuário, como a maioria dos gateways rotativos faz, você obtém esse isolamento de graça. Segundo, retry (um int, ou uma RetryStrategy de curl_cffi.requests com atraso, backoff e jitter) só é executado novamente em uma exceção de transporte. Ele não tenta novamente um 403 ou 429 da maneira que o status_forcelist do urllib3 faz — esse loop ainda é seu para escrever. Os documentos de compatibilidade listam retries como não suportados, o que está desatualizado: o parâmetro foi adicionado na v0.15.0.

Aponte curl_cffi para um gateway residencial rotativo

Rotação por solicitação e assíncrono

curl_cffi anuncia asyncio com rotação de proxy em cada solicitação, e isso é literal: um argumento proxy= em uma chamada individual substitui o que a sessão mantém. Você raramente precisa de uma lista de proxies para explorar isso — um gateway rotativo atribui um novo servidor de saída no lado do servidor em cada conexão, então um único endpoint mais concorrência já é rotação. Onde você quer controle (um IP estável por trabalhador, por conta, por carrinho) coloque um token de sessão no nome de usuário e deixe o gateway fixar essa saída.

import asyncio
from curl_cffi import AsyncSession

GATE = "gate.quantumproxies.io:PORT"
URLS = ["https://httpbin.org/ip"] * 20

async def fetch(session, url, worker):
    # one sticky exit IP per worker; drop the -session- suffix for full rotation
    proxy = f"http://USER-session-{worker}:PASS@{GATE}"
    r = await session.get(url, proxy=proxy, timeout=30)
    return r.status_code, r.json()["origin"]

async def main():
    async with AsyncSession(impersonate="chrome", max_clients=10) as s:
        return await asyncio.gather(
            *(fetch(s, u, i % 5) for i, u in enumerate(URLS))
        )

for status, ip in asyncio.run(main()):
    print(status, ip)

max_clients limita os handles curl concorrentes no pool (10 por padrão), então é seu verdadeiro controle de concorrência — adicionar um semáforo a um gather ilimitado é o erro usual. A mesma lógica de dimensionamento se aplica a qualquer cliente assíncrono, que cobrimos em raspagem assíncrona Python com httpx e aiohttp. Se deve rotacionar por solicitação ou fixar uma sessão depende de se o site rastreia estado entre solicitações; os trade-offs estão em sessões persistentes vs proxies rotativos.

Lista de verificação contrastando configurações de proxy curl_cffi funcionais com os cinco erros que as quebram
A maioria das falhas de proxy curl_cffi são uma de cinco coisas — e quatro delas são um único caractere em uma string.

SOCKS5, HTTP/3 e os interruptores de segurança

SOCKS não precisa de instalação extra — libcurl é compilado, então ao contrário de requests não há [socks] extra para lembrar. Use socks5h://USER:PASS@gate.quantumproxies.io:PORT: o h empurra a resolução DNS para o proxy, o que impede vazamentos da sua própria rede e resolve nomes de host geograficamente restritos a partir da localização de saída. curl_cffi detecta o prefixo socks e pula o flag de tunelamento HTTP, já que o protocolo SOCKS lida com isso sozinho. Todo plano aqui expõe endpoints HTTP e SOCKS5 no mesmo gateway, então a troca é uma mudança de esquema em vez de um novo pedido.

Quando curl_cffi mais um proxy é suficiente

Mais frequentemente do que as pessoas esperam. Se o alvo serve JSON de uma API interna ou HTML renderizado no servidor, e o único obstáculo é uma verificação de impressão digital, um handshake compatível mais uma saída residencial resolve isso a uma fração do custo e latência de um navegador. O FAQ do projeto é direto sobre o limite: impressões digitais são um fator entre vários, juntamente com a qualidade do IP, taxa de solicitações e verificações de JavaScript, e níveis de proteção mais altos precisam tanto de um pool de proxies melhor quanto de automação de navegador real. Quando a personificação está configurada corretamente e você ainda está bloqueado, a variável restante é quase sempre o IP de saída — isolar isso em cinco minutos é o assunto de curl_cffi vs requests. Se você preferir não executar nenhum dos dois, a Scraper API lida com impressões digitais, proxies e renderização JS opcional em uma única chamada.

Perguntas frequentes

Como uso um proxy com curl_cffi?

Passe proxy="http://USER:PASS@host:port" para qualquer método de solicitação, sessão ou sessão assíncrona. O dicionário no estilo requests proxies={"http": ..., "https": ...} também funciona, mas o projeto recomenda o parâmetro único a menos que você precise de proxies diferentes por esquema. Passar ambos gera um TypeError.

Por que curl_cffi lança ErrCode 35 WRONG_VERSION_NUMBER?

Porque a URL do proxy começa com https://. Um proxy padrão espera uma solicitação CONNECT em texto simples e depois canaliza seu TLS; um prefixo https:// faz curl tentar um handshake TLS com o próprio proxy, que responde em HTTP simples. Altere o valor para http:// — a chave https refere-se ao alvo, não ao salto.

curl_cffi suporta proxies SOCKS5?

Sim, nativamente — libcurl é incluído, então não há extra opcional para instalar. Use o esquema socks5h:// para que os nomes de host sejam resolvidos pelo proxy em vez da sua máquina. SOCKS4, SOCKS4a e socks5:// simples também são aceitos; a biblioteca pula o tunelamento HTTP para qualquer proxy cujo esquema comece com socks.

curl_cffi pode rotacionar proxies em cada solicitação?

Sim. Um argumento proxy= em uma chamada individual substitui o padrão da sessão, inclusive dentro de uma AsyncSession, que é o que o README quer dizer com asyncio com rotação por solicitação. Com um gateway rotativo, você geralmente não precisa de lógica alguma: o mesmo endpoint fornece um IP de saída diferente por conexão.

curl_cffi pode contornar Cloudflare?

Às vezes. Ele remove o indicativo de impressão digital TLS e HTTP/2, o que é suficiente para níveis básicos de proteção. Ele não pode executar desafios JavaScript, resolver Turnstile ou reparar um IP de datacenter que um banco de dados de reputação já sinalizou. Trate a personificação como um dos três requisitos, não a resposta.

Toda a configuração é menor do que sua reputação: uma string proxy, impersonate definida uma vez na sessão, um timeout em cada chamada, e credenciais ou codificadas na URL ou passadas como uma tupla proxy_auth. Acertar isso e a variável restante é a qualidade do IP — um handshake perfeito do Chrome de um endereço de datacenter sinalizado ainda é um endereço de datacenter sinalizado. Nossa análise de impressões digitais JA3 e JA4 explica por que as duas verificações são independentes.

Obtenha IPs residenciais que correspondem à sua personificação