Se você está fazendo scraping de sites ou automatizando requisições através de proxies em Python, mais cedo ou mais tarde você encontrará três
erros clássicos: ProxyError,
SSLError: WRONG_VERSION_NUMBER e HTTP
407 Proxy Authentication Required. Neste artigo, vamos analisar
a causa de cada um deles e mostrar um código funcional para correção.
Como requests trabalha com proxies: sintaxe básica
A biblioteca requests aceita proxies através de um dicionário
proxies, onde as chaves são os esquemas de requisição
(http e https),
e os valores são URLs do servidor proxy com autorização opcional.
import requests
proxies = {
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
Observe: mesmo para requisições HTTPS, o esquema do proxy geralmente é especificado como
http://, porque a maioria dos servidores proxy aceita
um túnel CONNECT através do protocolo HTTP, e não o encapsula em SSL. Se você especificar
https:// para um proxy que não suporta isso — você
receberá exatamente um SSLError, cuja análise será feita a seguir.
Se você estiver trabalhando com proxies SOCKS5, será necessário um pacote adicional:
pip install requests[socks]
# E a sintaxe de proxies muda:
proxies = {
"http": "socks5://user:password@proxy_host:port",
"https": "socks5://user:password@proxy_host:port",
}
ProxyError: causas e soluções
requests.exceptions.ProxyError é um wrapper para exceções de rede de baixo nível
que ocorrem ao tentar estabelecer uma conexão com o servidor proxy. Quatro cenários são mais comuns.
1. Proxy indisponível ou endereço/porta incorretos
requests.exceptions.ProxyError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.',
ConnectionResetError(104, 'Connection reset by peer')))
Verifique se o host e a porta estão corretos e se o servidor proxy está realmente ativo. A maneira mais rápida de diagnosticar é
tentar conectar através do curl:
curl -x http://user:password@proxy_host:port https://httpbin.org/ip -v
2. Proxy bloqueou sua requisição (ban por IP ou User-Agent)
Proxies de data center frequentemente são banidos por sites-alvo porque seus intervalos de IP são conhecidos por sistemas antifraude. Se o erro aparece consistentemente ao acessar um site específico (por exemplo, Wildberries, Ozon ou redes sociais), e não em todos os sites ao mesmo tempo — o problema não está no proxy, mas em sua reputação. Nesse caso, mudar para proxies residenciais pode ajudar — eles usam IPs de usuários reais e são menos propensos a serem bloqueados por sistemas antifraude.
3. Limite de conexões simultâneas excedido
Se você está executando scraping multithread e abrindo centenas de conexões através do mesmo proxy, o servidor pode
começar a descartar conexões extras. A solução é usar um pool de conexões através de
requests.adapters.HTTPAdapter com limites de
pool_maxsize e
max_retries:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(total=3, backoff_factor=1, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retries, pool_connections=20, pool_maxsize=20)
session.mount("http://", adapter)
session.mount("https://", adapter)
response = session.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
4. Timeout do lado do servidor proxy
Se o proxy "pendura" por mais tempo do que o timeout estabelecido, você verá
ProxyError, encapsulado em
ReadTimeoutError. Aumente o timeout para um valor razoável
(geralmente 15-30 segundos é suficiente) e adicione o tratamento de exceção separadamente:
try:
response = requests.get(url, proxies=proxies, timeout=20)
except requests.exceptions.ProxyError as e:
print(f"Problema com o proxy: {e}")
except requests.exceptions.Timeout:
print("O proxy não respondeu no tempo determinado")
SSLError WRONG_VERSION_NUMBER: análise do problema
Este erro é um dos mais incompreensíveis à primeira vista, mas na verdade tem quase sempre uma única causa.
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL:
WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)')))
WRONG_VERSION_NUMBER significa que o cliente tentou realizar
o handshake SSL/TLS onde o servidor espera tráfego HTTP normal (sem criptografia). A causa mais comum está em um esquema de proxy incorretamente
especificado no dicionário proxies.
Erro comum: especificar https:// em vez de
http:// no endereço do servidor proxy. A maioria dos proxies HTTP(S)
aceita a conexão via HTTP normal, mesmo quando proxyando tráfego HTTPS através do mecanismo CONNECT.
# INCORRETO — causará WRONG_VERSION_NUMBER
proxies = {
"http": "https://user:password@proxy_host:port",
"https": "https://user:password@proxy_host:port",
}
# CORRETO
proxies = {
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
}
A segunda causa comum é uma porta que fisicamente não suporta TLS. Por exemplo, se o provedor de proxy designou a porta 8080 apenas para tráfego HTTP, e você tenta estabelecer uma conexão TLS diretamente (pulando o CONNECT), o servidor responderá com texto simples em vez de um handshake TLS, e a biblioteca cliente OpenSSL gerará exatamente esse erro. Verifique na documentação do seu provedor de proxy quais portas e protocolos (HTTP/HTTPS/SOCKS5) estão disponíveis em cada porta específica.
A terceira opção é que você está usando socket ou bibliotecas de terceiros
para uma conexão TCP direta com o proxy, pulando o mecanismo padrão do requests. Nesse caso, certifique-se de que
a camada TLS é aplicada apenas ao site de destino real, e não à própria conexão com o proxy.
407 Proxy Authentication Required: autorização
O código de resposta 407 é análogo ao padrão 401, mas para servidores proxy. Isso significa que o servidor requer autorização, e você não a forneceu ou a forneceu em um formato incorreto.
Método 1: login e senha diretamente na URL
proxies = {
"http": "http://login:password@proxy_host:port",
"https": "http://login:password@proxy_host:port",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
Este método é o mais simples, mas se o login ou a senha contiverem caracteres especiais (por exemplo, @,
:, /),
eles precisam ser escapados através de urllib.parse.quote:
from urllib.parse import quote
login = quote("user@domain")
password = quote("p@ssw:ord!")
proxy_url = f"http://{login}:{password}@proxy_host:port"
proxies = {"http": proxy_url, "https": proxy_url}
Método 2: cabeçalho Proxy-Authorization manualmente
Se o provedor de proxy usar um esquema de autorização não padrão ou se você precisar de mais controle, pode montar o cabeçalho manualmente através da codificação base64:
import base64
import requests
login = "user"
password = "password"
credentials = base64.b64encode(f"{login}:{password}".encode()).decode()
headers = {
"Proxy-Authorization": f"Basic {credentials}"
}
proxies = {
"http": "http://proxy_host:port",
"https": "http://proxy_host:port",
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
headers=headers,
timeout=10
)
Importante: alguns provedores de proxy vinculam a autorização não ao login/senha, mas a um IP estático específico do cliente (whitelist). Se você mudar de IP (por exemplo, trabalhar através de VPN ou IP dinâmico do provedor), o erro 407 pode aparecer mesmo com credenciais corretas — porque seu IP externo atual não está na lista de permissões do proxy. A solução é acessar o painel do provedor e atualizar a lista de IPs permitidos, ou mudar para autorização por login/senha em vez de whitelist de IP.
Lógica de Retry e tratamento de exceções
Na prática, erros de proxy raramente são únicos — especialmente durante scraping em massa. A arquitetura correta do código deve prever tentativas de repetição com atraso e registro de cada erro separadamente.
import time
import requests
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def fetch_with_retry(url, proxies, max_retries=3, backoff=2):
for attempt in range(1, max_retries + 1):
try:
response = requests.get(url, proxies=proxies, timeout=15)
response.raise_for_status()
return response
except requests.exceptions.ProxyError as e:
logger.warning(f"[Tentativa {attempt}] ProxyError: {e}")
except requests.exceptions.SSLError as e:
logger.warning(f"[Tentativa {attempt}] SSLError: {e}")
except requests.exceptions.HTTPError as e:
if response.status_code == 407:
logger.error("407: verifique login/senha ou whitelist de IP do proxy")
break
logger.warning(f"[Tentativa {attempt}] HTTPError: {e}")
except requests.exceptions.Timeout:
logger.warning(f"[Tentativa {attempt}] Timeout de conexão")
time.sleep(backoff * attempt)
raise RuntimeError(f"Não foi possível obter {url} após {max_retries} tentativas")
Essa abordagem economiza horas de depuração: em vez de o script falhar na primeira falha de rede, você obtém um log claro indicando o tipo específico de erro em cada tentativa.
Rotação de proxies e combate a bans
Se ProxyError e 407 estão frequentemente relacionados às configurações do código, bans em massa e falhas de conexão estáveis em domínios específicos quase sempre indicam um problema com a qualidade do próprio pool de IPs. Para tarefas como scraping de marketplaces (Wildberries, Ozon, Avito) ou trabalho com redes sociais, recomenda-se a rotação de IP a cada requisição ou através de um intervalo determinado.
import random
proxy_pool = [
"http://user:[email protected]:8000",
"http://user:[email protected]:8000",
"http://user:[email protected]:8000",
]
def get_random_proxy():
proxy = random.choice(proxy_pool)
return {"http": proxy, "https": proxy}
response = requests.get(url, proxies=get_random_proxy(), timeout=15)
Para parsers em produção, geralmente é mais conveniente usar proxies com rotação automática do lado do provedor — assim não é necessário manter uma lista de IPs, a rotação ocorre de forma transparente através de um único endpoint. Nesse caso, proxies residenciais com rotação são especialmente adequados para contornar proteções anti-bot em sites com sistemas de detecção rigorosos, enquanto proxies de data center são uma boa escolha se a velocidade das requisições é importante e o site alvo não bloqueia agressivamente intervalos de data center.
| Tipo de proxy | Velocidade | Risco de ban | Tarefas |
|---|---|---|---|
| Data center | Alta | Médio-alto | Scraping rápido de APIs abertas |
| Residenciais | Média | Baixo | Scraping de marketplaces, redes sociais |
| Móveis | Média-baixa | Mínimo | Trabalho com sistemas anti-bot rigorosos |
Session, timeouts e verificação de SSL
Para scripts de longa duração (por exemplo, parsers cíclicos), é recomendável usar
requests.Session() em vez de chamadas separadas
requests.get(). A Session reutiliza conexões TCP,
o que reduz a carga no servidor proxy e diminui o número de ProxyErrors em grandes volumes de requisições.
session = requests.Session()
session.proxies.update({
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
})
for url in urls_list:
try:
resp = session.get(url, timeout=15)
print(resp.status_code, url)
except requests.exceptions.RequestException as e:
print(f"Erro em {url}: {e}")
Vale mencionar separadamente o parâmetro verify. Se você estiver trabalhando
com um proxy que altera o certificado SSL (proxy MITM para depuração ou alguns proxies corporativos),
o requests pode reclamar sobre a discrepância do certificado. Desativar a verificação completamente
(verify=False) deve ser feito apenas em um ambiente de teste — em produção
isso cria risco de ataques "homem no meio":
# Apenas para depuração local!
response = requests.get(url, proxies=proxies, verify=False, timeout=15)
# A maneira correta — especificar o caminho para o certificado confiável do proxy
response = requests.get(url, proxies=proxies, verify="/path/to/proxy_cert.pem")
Checklist de diagnóstico de erros
Se o script falhar com um erro de proxy, passe por esta lista de cima para baixo — em 90% dos casos a causa será encontrada nos primeiros três itens.
- Verifique o esquema no dicionário
proxies— quase sempre deve serhttp://, mesmo para requisições HTTPS. - Teste o proxy diretamente através de
curl -x, para excluir problemas do lado do código. - Certifique-se de que o login e a senha não contenham caracteres especiais não escapados na URL.
- Verifique se o proxy não está vinculado a um IP específico (whitelist) em vez de autorização por login/senha.
- Aumente o
timeoutpara 15-30 segundos antes de considerar o proxy inoperante. - Se o erro aparecer apenas em um site específico — o problema está na reputação do IP, e não no código.
- Adicione lógica de retry com atraso exponencial em vez de tentativas únicas.
- Durante scraping em massa, use rotação de IP, e não um único proxy estático para todas as requisições.
Conclusão
A maioria dos erros de proxy em Python requests se resume a três coisas: esquema incorreto na URL do proxy
(http em vez de https),
falta ou formato incorreto de autorização, e qualidade do próprio pool de IPs. ProxyError geralmente indica um problema
de conexão ou banimento do IP, SSLError WRONG_VERSION_NUMBER quase sempre aponta para um esquema de proxy incorreto, e 407 —
para um problema com login/senha ou whitelist.
Se você frequentemente enfrenta bans e erros 407 ao fazer scraping de marketplaces ou redes sociais, é hora de reconsiderar a fonte do proxy, e não apenas o código. Para scraping estável com baixa taxa de bloqueios, recomendamos experimentar proxies residenciais — eles usam IPs reais e raramente causam ProxyError em sites protegidos. Se a prioridade é a velocidade e o volume de requisições, e não contornar sistemas anti-bot complexos, considere proxies de data center — eles são mais baratos e mais rápidos para tarefas simples de scraping.