Cómo evitar el error HTTP 429 (demasiadas solicitudes) Python

94

Estoy tratando de usar Python para iniciar sesión en un sitio web y recopilar información de varias páginas web y obtengo el siguiente error:

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

Lo usé time.sleep()y funciona, pero parece poco inteligente y poco confiable, ¿hay alguna otra forma de esquivar este error?

Aquí está mi 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
fuente
6
No hay forma de evitarlo, se trata de una aplicación en el lado del servidor que lleva un registro de cuántas solicitudes / unidad de tiempo realiza. Si excede esta unidad, será bloqueado temporalmente. Algunos servidores envían esta información en el encabezado, pero esas ocasiones son raras. Verifique los encabezados recibidos del servidor, use la información disponible. Si no, verifique qué tan rápido puede martillar sin ser atrapado y use un sleep.
Torxed

Respuestas:

158

Recibir un estado 429 no es un error , es el otro servidor "amablemente" pidiéndole que deje de enviar solicitudes de spam. Obviamente, su tasa de solicitudes ha sido demasiado alta y el servidor no está dispuesto a aceptar esto.

No debe tratar de "esquivar" esto, o incluso tratar de eludir la configuración de seguridad del servidor tratando de falsificar su IP, simplemente debe respetar la respuesta del servidor al no enviar demasiadas solicitudes.

Si todo está configurado correctamente, también habrá recibido un encabezado "Reintentar después" junto con la respuesta 429. Este encabezado especifica la cantidad de segundos que debe esperar antes de realizar otra llamada. La forma correcta de lidiar con este "problema" es leer este encabezado y suspender el proceso durante tantos segundos.

Puede encontrar más información sobre el estado 429 aquí: http://tools.ietf.org/html/rfc6585#page-3

ARM
fuente
23
Bueno, nadie dijo nunca que todos los servidores web estén configurados correctamente. Además, dado que la mayoría de los limitadores de velocidad identifican a los visitantes por IP, esto podría generar problemas en un escenario en el que las IP se comparten de forma dinámica. Si sigue recibiendo el estado 429, aunque está seguro de que no ha enviado demasiadas solicitudes, puede considerar ponerse en contacto con el administrador del sitio.
MRA
2
Gracias por mencionar el encabezado "Reintentar después". Me encantaría un ejemplo de código para ver cómo obtener ese valor (estaba usando urllib, para mecanizar OP, en cualquier caso, no creo que los encabezados estén incluidos en la excepción planteada)
MacFreek
@MacFreek No tengo ningún ejemplo de código de Python en particular listo, pero supongo que se pueden tomar algunos ejemplos sobre cómo recuperar los encabezados de respuesta en general de las respuestas a esta pregunta: stackoverflow.com/q/843392
MRA
Gracias @MRA. Descubrí que los encabezados también están disponibles en la excepción: después de la captura HTTPError as my_exception, está disponible my_exception.headers, al menos para urllib2.
MacFreek
38

Escribir este código solucionó mi problema:

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

tadm123
fuente
26
Esta respuesta tiene una votación negativa, pero algunos sitios devuelven automáticamente el código de error 429 si el agente de usuario está prohibido debido al abuso de otras personas. Si obtiene el código de error 429 incluso si solo ha enviado algunas solicitudes, intente configurar el agente de usuario en otra cosa.
Ferry Boender
7
También me gustaría agregar, algunos sitios simplemente rechazan las solicitudes a menos que se envíe un agente de usuario, y puede obtener una miríada de otras respuestas: 503/403 / alguna página de índice genérica.
user3791372
1
Puedo confirmar esto.
Intentando
1
¿Puede agregar alguna explicación por favor?
Tokci
¿Dónde "escribe este fragmento de código"? Esta solución necesita más detalles.
Joe McLean
29

Como dijo MRA, no debe intentar esquivar un, 429 Too Many Requestssino manejarlo en consecuencia. Tiene varias opciones según su caso de uso:

1) Duerme tu proceso . El servidor generalmente incluye un Retry-afterencabezado en la respuesta con la cantidad de segundos que se supone que debe esperar antes de volver a intentarlo. Tenga en cuenta que dormir un proceso puede causar problemas, por ejemplo, en una cola de tareas, donde debería volver a intentar la tarea en un momento posterior para liberar al trabajador para otras cosas.

2) Retroceso exponencial . Si el servidor no le dice cuánto tiempo debe esperar, puede volver a intentar su solicitud utilizando pausas cada vez mayores en el medio. La popular cola de tareas Apio tiene esta función incorporada .

3) Cubo de fichas . Esta técnica es útil si sabe de antemano cuántas solicitudes puede realizar en un tiempo determinado. Cada vez que accede a la API, primero obtiene un token del depósito. El cubo se rellena a un ritmo constante. Si el depósito está vacío, sabrá que tendrá que esperar antes de volver a acceder a la API. Los depósitos de tokens generalmente se implementan en el otro extremo (la API), pero también puede usarlos como proxy para evitar obtener un 429 Too Many Requests. La función rate_limit de Celery utiliza un algoritmo de cubeta de tokens.

A continuación, se muestra un ejemplo de una aplicación Python / Celery que utiliza un retroceso exponencial y un depósito de token / limitador de velocidad:

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
fuente
9

Otra solución sería falsificar su IP utilizando algún tipo de red VPN pública o Tor. Esto supondría la limitación de velocidad en el servidor a nivel de IP.

Hay una breve publicación en el blog que demuestra una forma de usar tor junto con urllib2:

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

Gaurav Agarwal
fuente
8
Es por eso que siempre solicito a los usuarios de mis API que se registren para obtener una clave para realizar solicitudes. De esta manera puedo limitar las solicitudes por clave en lugar de por IP. Registrarse para otra clave sería la única forma de obtener un límite más alto.
Mnebuerquo
4
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
David Brown
fuente
1

Descubrí una buena solución para el bloqueo de IP al raspar sitios. Te permite ejecutar un Scraper indefinidamente ejecutándolo desde Google App Engine y volviéndolo a implementar automáticamente cuando obtienes un 429.

Mira este artículo

Juan Luis Ruiz-tagle
fuente
Jaja wow ... usando Google para raspar Google. Y luego cambiar su IP de Google cuando Google la bloquea.
sam1370 hace