Como evitar o erro HTTP 429 (muitas solicitações) python

91

Estou tentando usar Python para fazer login em um site e reunir informações de várias páginas da web e recebo o seguinte erro:

Traceback (most recent call last):
  File "extract_test.py", line 43, in <module>
    response=br.open(v)
  File "/usr/local/lib/python2.7/dist-packages/mechanize/_mechanize.py", line 203, in open
    return self._mech_open(url, data, timeout=timeout)
  File "/usr/local/lib/python2.7/dist-packages/mechanize/_mechanize.py", line 255, in _mech_open
    raise response
mechanize._response.httperror_seek_wrapper: HTTP Error 429: Unknown Response Code

Usei time.sleep()e funciona, mas parece pouco inteligente e confiável, existe alguma outra maneira de evitar esse erro?

Este é meu código:

import mechanize
import cookielib
import re
first=("example.com/page1")
second=("example.com/page2")
third=("example.com/page3")
fourth=("example.com/page4")
## I have seven URL's I want to open

urls_list=[first,second,third,fourth]

br = mechanize.Browser()
# Cookie Jar
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)

# Browser options 
br.set_handle_equiv(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)

# Log in credentials
br.open("example.com")
br.select_form(nr=0)
br["username"] = "username"
br["password"] = "password"
br.submit()

for url in urls_list:
        br.open(url)
        print re.findall("Some String")
Aous1000
fonte
6
Não há como contornar isso, esta é uma aplicação no lado do servidor para controlar quantas solicitações / unidade de tempo você faz. Se você exceder esta unidade, você será temporariamente bloqueado. Alguns servidores enviam essas informações no cabeçalho, mas essas ocasiões são raras. Verifique os cabeçalhos recebidos do servidor, use as informações disponíveis. Caso contrário, verifique a rapidez com que você pode martelar sem ser pego e use a sleep.
Torxed 01 de

Respostas:

158

Receber um status 429 não é um erro , é o outro servidor "gentilmente" pedindo que você pare de enviar solicitações de spam. Obviamente, sua taxa de solicitações tem sido muito alta e o servidor não está disposto a aceitar isso.

Você não deve tentar "se esquivar" disso, ou mesmo tentar burlar as configurações de segurança do servidor tentando falsificar seu IP; você deve simplesmente respeitar a resposta do servidor, não enviando muitas solicitações.

Se tudo estiver configurado corretamente, você também receberá um cabeçalho "Tentar novamente depois" junto com a resposta 429. Este cabeçalho especifica o número de segundos que você deve esperar antes de fazer outra chamada. A maneira adequada de lidar com esse "problema" é ler este cabeçalho e colocar o processo em espera por tantos segundos.

Você pode encontrar mais informações sobre o status 429 aqui: http://tools.ietf.org/html/rfc6585#page-3

MRA
fonte
23
Bem, ninguém nunca disse que todos os servidores da web estão configurados corretamente. Além disso, como a maioria dos limitadores de taxa identificam os visitantes por IP, isso pode levar a problemas em um cenário em que os IPs são compartilhados dinamicamente. Se você continuar recebendo o status 429, embora tenha certeza de que não enviou muitas solicitações, considere entrar em contato com o administrador do site.
MRA
2
Obrigado por mencionar o cabeçalho "Tentar novamente depois". Eu adoraria um exemplo de código para ver como obter esse valor (eu estava usando urllib, para mecanizar OP, em qualquer caso, não acho que os cabeçalhos estão incluídos na exceção levantada)
MacFreek
@MacFreek Não tenho nenhum exemplo de código Python específico pronto, mas presumo que alguns exemplos sobre como recuperar cabeçalhos de resposta em geral podem ser obtidos nas respostas a esta pergunta: stackoverflow.com/q/843392
MRA
Obrigado @MRA. Descobri que os cabeçalhos também estão disponíveis na exceção: depois de capturar HTTPError as my_exception, ele está disponível em my_exception.headers, pelo menos para urllib2.
MacFreek
37

Escrever este trecho de código corrigiu meu problema:

requests.get(link, headers = {'User-agent': 'your bot 0.1'})

tadm123
fonte
26
Esta resposta é rejeitada, mas alguns sites retornam automaticamente o código de erro 429 se o agente do usuário for banido devido ao abuso de outras pessoas. Se você obtiver o código de erro 429 mesmo que tenha enviado apenas algumas solicitações, tente configurar o agente do usuário para outra coisa.
Ferry Boender 01 de
7
Também gostaria de acrescentar, alguns sites recusam completamente as solicitações, a menos que um agente do usuário seja enviado, e você pode obter uma miríade de outras respostas: 503/403 / alguma página de índice genérica.
user3791372
1
Posso confirmar isso. Apenas tentando fazer a interface do python com o reddit e sem definir o agente do usuário, eu sempre recebia o código de erro 429.
Karrq
você pode adicionar alguma explicação, por favor?
Tokci
29

Como disse o MRA, você não deve tentar se esquivar de um, 429 Too Many Requestsmas lidar com ele de acordo. Você tem várias opções, dependendo do seu caso de uso:

1) Durma seu processo . O servidor geralmente inclui um Retry-aftercabeçalho na resposta com o número de segundos que você deve esperar antes de tentar novamente. Lembre-se de que suspender um processo pode causar problemas, por exemplo, em uma fila de tarefas, onde você deve repetir a tarefa mais tarde para liberar o trabalhador para outras coisas.

2) Backoff exponencial . Se o servidor não informar quanto tempo você deve esperar, você pode repetir a solicitação usando pausas crescentes no meio. A popular fila de tarefas do Celery tem esse recurso integrado .

3) Balde de token . Essa técnica é útil se você sabe com antecedência quantas solicitações pode fazer em um determinado momento. Cada vez que você acessa a API, primeiro você busca um token do intervalo. O balde é reabastecido a uma taxa constante. Se o balde estiver vazio, você sabe que terá que esperar antes de acessar a API novamente. Os depósitos de token geralmente são implementados na outra extremidade (a API), mas você também pode usá-los como um proxy para evitar a obtenção de um 429 Too Many Requests. O recurso rate_limit do Celery usa um algoritmo de token bucket.

Aqui está um exemplo de um aplicativo Python / Celery usando backoff exponencial e limitação de taxa / token bucket:

class TooManyRequests(Exception):
"""Too many requests"""

@task(
   rate_limit='10/s',
   autoretry_for=(ConnectTimeout, TooManyRequests,),
   retry_backoff=True)
def api(*args, **kwargs):
  r = requests.get('placeholder-external-api')

  if r.status_code == 429:
    raise TooManyRequests()
psaniko
fonte
9

Outra solução alternativa seria falsificar seu IP usando algum tipo de VPN pública ou rede Tor. Isso pressupõe a limitação de taxa no servidor no nível de IP.

Há uma breve postagem no blog que demonstra uma maneira de usar tor junto com urllib2:

http://blog.flip-edesign.com/?p=119

Gaurav Agarwal
fonte
8
É por isso que sempre exijo que os usuários de minhas APIs se registrem para obter uma chave para fazer solicitações. Dessa forma, posso limitar as solicitações por chave em vez de por IP. O registro de outra chave seria a única maneira de obter um limite mais alto.
Mnebuerquo
2
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
davidbrown
fonte