Sempre que estiver lidando com HTTP, as solicitações com falha são um fato inevitável da realidade com o qual é preciso lidar. No desenvolvimento da Web, o status 200 indica uma boa resposta. No entanto, nem sempre recebemos um 200, e este guia o ajudará a entender como lidar com esses códigos de status não 200.
De acordo com a Mozilla, os códigos de status podem ser divididos nas seguintes categorias:
- 100-199: Respostas informativas
- 200-299: Respostas bem-sucedidas
- 300-399: Mensagens de redirecionamento
- 400-499: Mensagens de erro do cliente
- 500-599: Mensagens de erro do servidor
O que são códigos de status?
Os códigos de erro são importantes. Ao criar programas do lado do cliente, como raspadores da Web, precisamos nos concentrar principalmente nos códigos de status nas faixas de 400+ e 500+. Os códigos na faixa de 400 geralmente abrangem erros no lado do cliente, como problemas de autenticação, limitação de taxa, timeouts e o infame erro 404: Arquivo não encontrado. Nos códigos 500, geralmente estamos analisando problemas no servidor.
Há décadas, a Mozilla vem documentando os padrões de desenvolvimento da Web do W3C e do IETF. Abaixo está uma lista de códigos de erro comuns que você pode encontrar. Essa lista não é exaustiva. Esses erros são provenientes da documentação oficial da Mozilla. Dependendo do seu site de destino, seus códigos podem ser ligeiramente diferentes, mas a lógica deve permanecer a mesma.
Código de status | Significado | Descrição |
---|---|---|
400 | Solicitação ruim | Verifique o formato de sua solicitação |
401 | Não autorizado | Verifique sua chave de API |
403 | Proibido | Não é possível acessar esses dados |
404 | Não encontrado | O site/ponto de extremidade não existe |
408 | Tempo limite da solicitação | A solicitação expirou, tente novamente |
429 | Muitas solicitações | Diminua a velocidade de suas solicitações |
500 | Erro interno do servidor | Erro genérico do servidor, solicitação de nova tentativa |
501 | Não implementado | O servidor ainda não é compatível com isso |
502 | Gateway ruim | Falha na resposta de um servidor upstream |
503 | Serviço indisponível | O servidor está temporariamente fora do ar, tente novamente mais tarde |
504 | Tempo limite do gateway | Tempo limite de espera por um servidor upstream |
Estratégias de repetição
Ao implementar um mecanismo de repetição, você pode usar bibliotecas pré-construídas, como HTTPAdapter e Tenacity. Dependendo do seu caso, você pode até querer escrever sua própria lógica de repetição.
Normalmente, queremos um limite de tentativas e uma estratégia para recuar. Precisamos de um limite para não ficarmos presos em um loop infinito de novas tentativas. Precisamos recuar pouco a pouco para respeitar o servidor host. Quando as solicitações são muito rápidas, elas fazem com que você seja bloqueado ou sobrecarregam o servidor.
- Limites de repetição: Você precisa definir um limite. Após um número X de tentativas, seu scraper desistirá.
- Algoritmo de backoff: Este é relativamente simples. Você quer começar com um backoff pequeno e aumentá-lo a cada nova tentativa. Queremos começar com 0,3, depois aumentar para 0,6, 1,2 e assim por diante.
Queremos tentar novamente nossas solicitações até um determinado limite. Após cada solicitação com falha, queremos esperar um pouco mais.
Adaptador HTTPA
Com o HTTPAdapter, precisamos configurar três coisas: total
, backoff_factor
e status_forcelist
. allowed_methods
não é realmente um requisito, mas torna nosso código mais seguro ao ajudar a definir nossas condições de repetição. No código abaixo, usamos o httpbin para forçar automaticamente um erro e acionar nossa lógica de repetição.
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
#create a session
session = requests.Session()
#configure retry settings
retry = Retry(
total=3, #maximum retries
backoff_factor=0.3, #time between retries (exponential backoff)
status_forcelist=(429, 500, 502, 503, 504), #status codes to trigger a retry
allowed_methods={"GET", "POST"}
)
#mount the adapter with our custom settings
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
#actually make a request with our retry logic
try:
print("Making a request with retry logic...")
response = session.get("https://httpbin.org/status/500")
response.raise_for_status()
print("✅ Request successful:", response.status_code)
except requests.exceptions.RequestException as e:
print("❌ Request failed after retries:", e)
Depois de criarmos um objeto Session
, fazemos o seguinte:
- Crie um objeto
Retry
e defina o seguinte:total
: o limite máximo para tentar novamente uma solicitação.backoff_factor
: Tempo de espera entre novas tentativas. Isso se ajusta exponencialmente à medida que nossas tentativas aumentam.status_forcelist
: Uma lista de códigos de status ruins. Qualquer código dessa lista acionará automaticamente uma nova tentativa.
- Crie um objeto
HTTPAdapter
com nossa variávelretry
:adapter = HTTPAdapter(max_retries=retry)
. - Depois de criarmos o
adaptador
, nós o montamos nos métodos HTTP e HTTPS usandosession.mount()
.
Quando você executar esse código, nossas três tentativas(total=3
) serão executadas e, em seguida, você obterá o seguinte resultado.
Making a request with retry logic...
❌ Request failed after retries: HTTPSConnectionPool(host='httpbin.org', port=443): Max retries exceeded with url: /status/500 (Caused by ResponseError('too many 500 error responses'))
Tenacidade
Você também pode usar o Tenacity, uma popular biblioteca de tentativas de código aberto para Python. Ela não se limita ao HTTP, mas nos oferece uma maneira expressiva e compreensível de implementar tentativas.
Primeiro, você precisa instalá-lo.
pip install tenacity
Depois de instalado, criamos um decorador e o usamos para envolver uma função de solicitação. Com nosso decorador @retry
, adicionamos os argumentos stop
, wait
e retry
.
import requests
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type, RetryError
#define a retry strategy
@retry(
stop=stop_after_attempt(3), #retry up to 3 times
wait=wait_exponential(multiplier=0.3), #exponential backoff
retry=retry_if_exception_type(requests.exceptions.RequestException), #retry on request failures
)
def make_request():
print("Making a request with retry logic...")
response = requests.get("https://httpbin.org/status/500")
response.raise_for_status()
print("✅ Request successful:", response.status_code)
return response
# Attempt to make the request
try:
make_request()
except RetryError as e:
print("❌ Request failed after all retries:", e)
A lógica e as configurações aqui são muito semelhantes às do nosso primeiro exemplo com o HTTPAdapter.
stop=stop_after_attempt(3)
: Isso diz aotenacity
para desistir após 3 tentativas fracassadas.wait=wait_exponential(multiplier=0.3)
usa a mesma espera que usamos anteriormente. Ele também recua exponencialmente, exatamente como antes.retry=retry_if_exception_type(requests.exceptions.RequestException)
diz aotenacity
para usar essa lógica sempre que ocorrer umaRequestException
.make_request()
faz uma solicitação ao nosso endpoint de erro. Ele recebe todas as características do decorador que criamos acima dele.
Quando você executa esse código, obtém um resultado semelhante.
Making a request with retry logic...
Making a request with retry logic...
Making a request with retry logic...
❌ Request failed after all retries: RetryError[<Future at 0x75e762970760 state=finished raised HTTPError>]
Crie seu próprio mecanismo de repetição
Você também pode criar seu próprio mecanismo de repetição. Ao lidar com código personalizado, essa pode ser a melhor abordagem. Com uma quantidade relativamente pequena de código, podemos obter o mesmo efeito que obtemos com essas bibliotecas.
No código abaixo, precisamos importar o sleep
para nosso backoff exponencial. Mais uma vez, definimos nossa configuração: total
, backoff_factor
e bad_codes
. Em seguida, usamos um loop while
para manter nossa lógica de repetição. Enquanto
ainda temos tentativas e não obtivemos êxito, tentamos a solicitação.
import requests
from time import sleep
#create a session
session = requests.Session()
#define our retry settings
total = 3
backoff_factor = 0.3
bad_codes = [429, 500, 502, 503, 504]
#try counter and success boolean
current_tries = 0
success = False
#attempt until we succeed or run out of tries
while current_tries < total and not success:
try:
print("Making a request with retry logic...")
response = session.get("https://httpbin.org/status/500")
if response.status_code in bad_codes:
raise requests.exceptions.HTTPError(f"Received {response.status_code}, triggering retry")
print("✅ Request successful:", response.status_code)
success = True
except requests.exceptions.RequestException as e:
print(f"❌ Request failed: {e}, retries left: {total-current_tries}")
sleep(backoff_factor)
backoff_factor = backoff_factor * 2
current_tries+=1
A lógica real aqui é tratada por um simples loop while
.
- Se
response.status_code
estiver em nossa lista debad_codes
, lançaremos uma exceção. - Se uma solicitação falhar, nós:
- Imprime uma mensagem de erro no console.
sleep(backoff_factor)
aguarda antes de enviar a próxima solicitação.backoff_factor = backoff_factor * 2
duplica nossobackoff_factor
para a próxima tentativa.- Nós incrementamos
current_tries
para não ficarmos no loop indefinidamente.
Aqui está o resultado de nossa lógica de repetição personalizada.
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 3
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 2
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 1
Como superar bloqueios
Na natureza, alguns sites vão bloquear você. A prática recomendada é sempre usar um proxy com solicitações Python. Com um proxy, sua solicitação é roteada por uma máquina diferente. Isso protegerá sua identidade e evitará que seu endereço IP seja bloqueado pelo site de destino. Temos até um guia detalhado sobre como contornar bloqueios de IP. Nossos proxies residenciais foram criados para que você supere esses desafios.
Conclusão
Agora você sabe como lidar com solicitações HTTP com falha no Python. Se estiver escrevendo um scraper, um cliente de API ou ferramentas de automação, você saberá como lidar com esses problemas. Para evitar todos os tipos de solicitações com falha, desenvolvemos produtos como a API Web Unlocker e o Scraping Browser. Essas ferramentas lidam automaticamente com medidas antibot, desafios CAPTCHA e bloqueios de IP, garantindo uma raspagem da Web perfeita e eficiente até mesmo para os sites mais desafiadores.
Inscreva-se agora e comece seu teste gratuito hoje mesmo.