Voltar ao blogue
Guias
Ștefan RăcilăLast updated on May 7, 202611 min read

Como usar proxies com Python Requests: Do básico à produção

Como usar proxies com Python Requests: Do básico à produção
Resumo: Este guia explica passo a passo como utilizar proxies com o Python Requests de ponta a ponta: um proxies , URLs autenticadas, variáveis de ambiente, Session reutilização, SOCKS5 sem fugas de DNS e um conjunto de rotação com tentativas de reenvio e um disjuntor de circuito. No final, saberá quando uma API gerida compensa em relação a um conjunto DIY.

Introdução

Se já lançou um scraper que funcionava localmente e depois começou a devolver erros 403, 429 ou timeouts silenciosos em produção, já sabe por que razão os proxies existem. Aprender a utilizar proxies com o Python Requests é a diferença entre um script que é executado uma vez no seu portátil e uma tarefa que sobrevive a limites de taxa, bloqueios geográficos e proibições de IP em milhares de páginas.

Uma configuração de proxy do Python Requests, na sua forma mais simples, é um dicionário que mapeia http e https a um URL de proxy e é passado para requests.get(). Isso permite-lhe desbloquear o acesso durante dez minutos. A produção precisa de mais: credenciais mantidas fora do git, sessões que mantêm cookies, pontos finais SOCKS5 que não vazam DNS, tentativas com backoff e uma estratégia de rotação que não continue a martelar um proxy inativo.

Este guia destina-se a programadores Python de nível intermédio que já conhecem os conceitos básicos de requests e agora precisam de um caminho fiável para adicionar suporte a proxies sem reescrever o seu scraper. Abordamos como usar proxies com o Python Requests, desde o dicionário trivial até um ciclo de rotação de produção, com as vantagens e desvantagens explicadas em linguagem simples.

Início rápido: um proxy Python Requests funcional em cinco minutos

Antes de nos aprofundarmos na rotação e nas tentativas, eis o exemplo de oito linhas de que 90% dos programadores realmente precisam quando procuram como usar proxies com Python Requests. Coloque-o num ficheiro, substitua por qualquer proxy host:port que funcione e execute.

import requests

proxies = {
    "http":  "http://203.0.113.10:8080",
    "https": "http://203.0.113.10:8080",
}

resp = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10)
print(resp.json())

Se o IP que for apresentado for o endereço do proxy e não o seu, o seu proxy está no caminho da solicitação. O resto deste guia trata de como reforçar este padrão.

Pré-requisitos: Python, pip e um proxy ao qual consiga aceder

Precisa do Python 3.8 ou posterior (python --version), pipe, pelo menos, um host:porta de proxy utilizável. Um ambiente virtual (python -m venv venv) mantém as dependências organizadas por projeto. Instale o Requests com pip install requests. O proxy pode provir de uma lista gratuita, de um conjunto pago ou de uma instância local do Squid ou do Tor.

Como usar proxies com o Python Requests: o modelo mental

Antes de se debruçar sobre o código, é útil saber como o Requests decide, na verdade, para onde enviar o tráfego. A biblioteca encaminha cada chamada através de um URL de proxy com base no esquema: HTTP, HTTPS e (com um pacote adicional) SOCKS. Três fontes podem fornecer esse URL, nesta ordem aproximada de precedência: o proxies= argumento numa única chamada, o session.proxies dict num Sessione, finalmente, as HTTP_PROXY / HTTPS_PROXY variáveis de ambiente. A precedência exata e o tratamento de variantes em minúsculas estão documentados na documentação de utilização avançada do Requests; confirme sempre com a sua versão fixada.

Configurar um proxy básico com o Python Requests

A configuração básica envolve duas etapas: criar um proxies dicionário e, em seguida, enviar um pedido de verificação através dele. As duas subsecções seguintes explicam cada etapa e as armadilhas que surgem em proxies inativos ou mal configurados.

Crie o dicionário de proxies para HTTP e HTTPS

No Python Requests, os proxies são passados como um dicionário que mapeia esquemas para um URL de proxy. Preencha sempre ambas as chaves, mesmo quando pretender aceder apenas a destinos HTTPS, porque os redirecionamentos podem fazer com que o esquema seja rebaixado.

proxies = {
    "http":  "http://user:pass@proxy.example.com:8080",
    "https": "http://user:pass@proxy.example.com:8080",
}
requests.get(url, proxies=proxies, timeout=(5, 15))

O timeout=(connect, read) tupla é imprescindível em produção. Sem ela, um proxy inativo bloqueia o seu worker.

Confirme se o proxy está no caminho da solicitação

Aceda a um ponto final de eco de IP e compare com o seu IP real. Dois pontos finais fiáveis são https://api.ipify.org?format=json e https://httpbin.org/ip.

print(requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10).json())

Se o endereço devolvido for diferente do seu IP local, o proxy está a funcionar. Se corresponder, o proxy falhou silenciosamente na abertura.

Autenticar proxies e proteger credenciais

A maioria dos proxies pagos é autenticada, e é aí que a utilização de proxies com Python Requests se torna mais complicada. As próximas três subsecções abordam a incorporação de URL, variáveis de ambiente e os três códigos de erro que irá encontrar.

Incorporar um nome de utilizador e uma palavra-passe na URL do proxy

O formato aceitável é http://user:pass@host:port. Se a sua palavra-passe contiver @, :, %, ou /, codifique-a para URL, caso contrário o Requests irá interpretar mal a URL e irá ver erros 407:

from urllib.parse import quote
user = quote("alice@corp")
pwd  = quote("p@ss:w/rd%1")
proxy_url = f"http://{user}:{pwd}@proxy.example.com:8080"

Nunca suba essa string para o git.

Mova os segredos para HTTP_PROXY, HTTPS_PROXY e NO_PROXY

O Requests deteta automaticamente HTTP_PROXY, HTTPS_PROXY, e NO_PROXY do ambiente e, de acordo com a documentação oficial, também aceita variantes em minúsculas em sistemas POSIX. Isso significa que pode manter as credenciais totalmente fora do código:

# Linux / macOS
export HTTPS_PROXY="http://user:pass@proxy.example.com:8080"
export NO_PROXY="localhost,127.0.0.1,.internal"
# Windows
setx HTTPS_PROXY "http://user:pass@proxy.example.com:8080"

Este é o padrão mais limpo para imagens Docker e executores de CI, onde os segredos residem no ambiente e não no repositório.

Diagnosticar erros de proxy 407, 401 e 403

Quando algo está errado, o código de estado indica-lhe qual a camada que está com problemas.

Estado

Causa provável

Correção em uma linha

407 Autenticação de proxy necessária

Credenciais de proxy em falta ou incorretas

Codifique a senha em URL e teste novamente

401 Não autorizado

Nome de utilizador ou palavra-passe incorretos

Altere as credenciais e verifique com curl -x

403 Proibido

O site de destino bloqueou o IP do proxy

Mude para outro proxy ou altere a localização geográfica

Verifique primeiro o proxy e, em seguida, o destino.

Reutilize as configurações com requests.Session para cookies e pool de conexões

A Session é a primitiva certa assim que fizer mais do que uma chamada. Mantém proxies, cabeçalhos padrão e cookies, e mantém a ligação TCP subjacente ativa para que não tenha de pagar por um novo handshake TLS a cada acesso. A sessão está integrada no Requests, pelo que não há nada extra para instalar.

session = requests.Session()
session.proxies = proxies
session.headers.update({"User-Agent": "my-scraper/1.0"})

session.post("https://example.com/login", data={"u": "alice", "p": "secret"})
dashboard = session.get("https://example.com/dashboard")  # cookies persist
print(dashboard.status_code, len(dashboard.content))

A mesma sessão abrange .text, .json(), e .content, pelo que os downloads de texto, JSON e binários passam todos pelo mesmo proxy de sessão do Python Requests sem necessidade de reconfiguração.

Use proxies SOCKS5 via requests[socks]

O Requests não suporta SOCKS de fábrica. Incorpora o PySocks com o socks extra:

pip install "requests[socks]"

Em seguida, use o socks5h:// scheme. O h indica ao PySocks para resolver o DNS através do proxy em vez de localmente, o que é o que se pretende quando não se confia no resolvedor do ISP ou se está a utilizar o Tor.

proxies = {
    "http":  "socks5h://127.0.0.1:9050",  # Tor default
    "https": "socks5h://127.0.0.1:9050",
}
requests.get("https://check.torproject.org/", proxies=proxies, timeout=15)

O socks5:// resolve o DNS localmente e revela discretamente os nomes de host que visita.

Alterne proxies para evitar bloqueios e limites de taxa

Um único IP fica sujeito a limites de taxa e, eventualmente, bloqueado. A verdadeira resposta para como usar proxies com Python Requests em escala é a rotação, e as próximas três subsecções mostram padrões de maturidade crescente.

Rotação aleatória com um ciclo de repetição

O padrão mais simples é random.choice sobre uma lista de proxies, envolvida num ciclo de tentativas:

import random, requests
from requests.exceptions import RequestException

PROXIES = [{"http": p, "https": p} for p in PROXY_URLS]

def fetch(url, attempts=4):
    for _ in range(attempts):
        proxy = random.choice(PROXIES)
        try:
            return requests.get(url, proxies=proxy, timeout=10)
        except RequestException:
            continue
    raise RuntimeError("all attempts failed")

Funciona, mas a aleatoriedade pura tende a escolher repetidamente proxies inativos e ignora a carga.

Escolhas em potências de dois para um balanceamento de carga mais inteligente

Um refinamento bem estudado é a escolha de potência de dois: para cada pedido, sorteia-se dois proxies aleatoriamente e utiliza-se aquele que está atualmente a tratar de menos chamadas em curso. A intuição, apoiada pela literatura sobre equilíbrio de carga comumente atribuída à análise de Mitzenmacher de 2001, é que isto atenua a carga no pior dos casos muito melhor do que a aleatoriedade uniforme, mantendo-se económico.

import random
LOAD = {p: 0 for p in PROXY_URLS}

def pick():
    a, b = random.sample(PROXY_URLS, 2)
    return a if LOAD[a] <= LOAD[b] else b

Aumente LOAD[proxy] antes da solicitação e diminua depois. Os ganhos exatos dependem do tamanho do pool; faça um benchmark antes de citar números.

Adicione um disjuntor para que os proxies inativos deixem de desperdiçar pedidos

Tanto o aleatório como o poder de dois continuam a selecionar um proxy inativo até que este tenha sucesso. Um disjuntor resolve isso. Acompanhe o estado por proxy: CLOSED (ativo), OPEN (ignorada) e HALF_OPEN (em período de prova).

import time
state = {p: {"fail": 0, "open_until": 0} for p in PROXY_URLS}
MAX_FAILS, COOLDOWN = 3, 60

def usable(p):
    return time.time() >= state[p]["open_until"]

def record(p, ok):
    if ok:
        state[p]["fail"] = 0
    else:
        state[p]["fail"] += 1
        if state[p]["fail"] >= MAX_FAILS:
            state[p]["open_until"] = time.time() + COOLDOWN

Após o tempo de espera, envie uma solicitação probatória ao proxy antes de o reativar totalmente.

Repetir pedidos falhados com HTTPAdapter e urllib3 Repetir

Montar um HTTPAdapter com uma urllib3 Retry política numa Sessão aplica tentativas de repetição a todas as chamadas HTTP e HTTPS dessa sessão. Fixar urllib3 (por exemplo, urllib3==2.2.*) para que os nomes dos parâmetros permaneçam estáveis ao longo das atualizações.

from requests import Session
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(
    total=3,
    status_forcelist=[429, 500, 502, 503, 504],
    backoff_factor=2,
    allowed_methods=["GET", "POST"],
    respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
s = Session()
s.mount("http://", adapter)
s.mount("https://", adapter)

Com backoff_factor=2, o urllib3 fica inativo por cerca de backoff_factor * (2 ** (n - 1)) segundos entre tentativas (cerca de 2, 4, 8 s). Combine as tentativas com a rotação para que cada nova tentativa também escolha um novo proxy.

Lidar com a verificação SSL e certificados de proxy autoassinados

Se um proxy apresentar um certificado autoassinado, verify=False ignora o aviso, mas deixa-o vulnerável a ataques man-in-the-middle, por isso use-o apenas em proxies locais de confiança ou em testes. A solução mais segura é adicionar o proxy ou o pacote de CAs corporativas ao armazenamento de confiança através de verify="/path/to/ca.pem" ou REQUESTS_CA_BUNDLE. Suprima InsecureRequestWarning apenas depois de ter feito deliberadamente a escolha de segurança.

Quando trocar o conjunto de proxies DIY por uma API de scraping gerida

Execute esta lista de verificação. Se marcar três ou mais itens, um proxy gerido ou uma API de scraping é normalmente mais barato do que o seu tempo:

  • Precisa de segmentação geográfica em mais de dois países.
  • Os bloqueios custam receitas reais, não apenas uma nova tentativa.
  • Os alvos renderizam conteúdo com JavaScript.
  • Um engenheiro sénior passa um dia por semana a supervisionar o conjunto de proxies.
  • A conformidade exige IPs residenciais auditados.

Pontos-chave

  • A resposta mais curta sobre como usar proxies com Python Requests é um dicionário que mapeia http e https a um URL de proxy, passado através proxies= com um timeout.
  • Mantenha as credenciais fora do código-fonte: prefira HTTP_PROXY, HTTPS_PROXY, e NO_PROXY variáveis de ambiente, e codificar por URL os caracteres especiais nas senhas.
  • Um requests.Session persiste proxies, cabeçalhos e cookies e reutiliza ligações TCP, o que é a configuração padrão adequada para qualquer fluxo de trabalho com múltiplas chamadas.
  • A rotação em produção combina opções de potência de dois com um disjuntor e uma HTTPAdapter Retry política que resiste a erros 429 e 5xx.
  • Para SOCKS5, instale requests[socks] e utilize socks5h:// para que o DNS seja resolvido através do proxy em vez de vazar localmente.

Recursos relacionados da WebScrapingAPI

Perguntas frequentes

O Python Requests suporta proxies SOCKS5 de forma nativa?

Não. A instalação base requests apenas inclui suporte para HTTP e HTTPS. Execute pip install "requests[socks]" para instalar o PySocks e, em seguida, use um socks5:// ou, de preferência, socks5h:// uma URL no seu proxies dict. Esse é o caminho mais simples para o suporte a SOCKS.

Por que é que as minhas solicitações por proxy continuam a revelar o meu IP real através de pesquisas de DNS?

Porque o socks5:// esquema diz ao PySocks para resolver nomes de host localmente antes de tunelar a ligação. Mude para socks5h://, onde o h significa resolução remota de nomes de host, de modo que as consultas DNS passam pelo servidor SOCKS. Isto é mais importante para o Tor ou qualquer modelo de ameaça em que o seu resolvedor DNS não seja confiável ou seja monitorado.

Como faço para codificar por URL uma senha de proxy que contém os caracteres @, : ou %?

Use urllib.parse.quote da biblioteca padrão: quote("p@ss:w/rd%1") torna-se p%40ss%3Aw%2Frd%251. Incorpore o valor codificado em http://user:encoded_pwd@host:port. Sem a codificação, esses caracteres encerram o segmento de informações do utilizador prematuramente, e irá receber um erro 407 Proxy Authentication Required mesmo quando a palavra-passe estiver tecnicamente correta.

Como faço para indicar ao Python Requests para ignorar o proxy para localhost ou domínios internos?

Defina NO_PROXY para uma lista separada por vírgulas de hosts ou sufixos de domínio, por exemplo NO_PROXY="localhost,127.0.0.1,.internal,.svc.cluster.local". O Requests respeita as variantes em maiúsculas e minúsculas em sistemas POSIX. Para substituições por chamada, passe proxies={"http": None, "https": None} para ignorar qualquer proxy ao nível da sessão.

Quando devo mudar de um conjunto de proxies rotativos DIY para uma API de scraping gerida?

Quando o custo operacional ultrapassa o valor da fatura. Gatilhos concretos: os bloqueios custam mais do que uma nova tentativa, precisa de IPs residenciais em vários países, os alvos têm muito JavaScript ou gasta mais do que algumas horas de engenharia por semana a ajustar o conjunto. Abaixo disso, um pequeno conjunto DIY com novas tentativas e um disjuntor geralmente é suficiente.

Conclusão

Saber como usar proxies com Python Requests tem menos a ver com um único truque e mais com camadas: um proxies para começar, credenciais em variáveis de ambiente para que os segredos fiquem fora do git, um Session para reutilização de ligações e cookies, socks5h:// quando as fugas de DNS importam, e rotação mais tentativas de nova ligação quando um IP já não é suficiente. Combine as opções de potência de dois com um disjuntor e uma HTTPAdapter Retry política, e o seu scraper deixa de falhar no momento em que um proxy fica inativo ou um alvo devolve erros 429.

A certa altura, todas as equipas chegam a um ponto em que gerir o conjunto de proxies custa mais do que o valor dos dados. Se os seus alvos forem fortemente protegidos, específicos de uma localização geográfica ou renderizados em JavaScript, uma opção gerida como a API WebScrapingAPI Scraper trata da camada de pedidos, rotação e desbloqueio por trás de um único ponto de acesso, para que possa manter o código de análise que já escreveu e apenas substituir a etapa de obtenção. Use a lista de verificação acima para decidir; se três ou mais itens estiverem marcados, a matemática favorece a infraestrutura gerida em vez de mais uma ronda de manutenção do conjunto. De qualquer forma, os padrões neste guia devem manter o seu requests-baseado em bom estado, desde o protótipo até à produção.

Sobre o autor
Ștefan Răcilă, Desenvolvedor Full Stack @ WebScrapingAPI
Ștefan RăcilăDesenvolvedor Full Stack

Stefan Racila é engenheiro de DevOps e Full Stack na WebScrapingAPI, onde desenvolve funcionalidades do produto e mantém a infraestrutura que garante a fiabilidade da plataforma.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.