So vermeiden Sie HTTP-Fehler 429 (Too Many Requests) Python

91

Ich versuche, mich mit Python auf einer Website anzumelden und Informationen von mehreren Webseiten zu sammeln. Dabei wird die folgende Fehlermeldung angezeigt:

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

Ich habe verwendet time.sleep()und es funktioniert, aber es scheint unintelligent und unzuverlässig. Gibt es eine andere Möglichkeit, diesem Fehler auszuweichen?

Hier ist mein Code:

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
quelle
6
Daran führt kein Weg vorbei. Dies ist eine Durchsetzung auf der Serverseite, bei der nachverfolgt wird, wie viele Anforderungen / Zeiteinheiten Sie stellen. Wenn Sie diese Einheit überschreiten, werden Sie vorübergehend blockiert. Einige Server senden diese Informationen im Header, aber diese Gelegenheiten sind selten. Überprüfen Sie die vom Server empfangenen Header, verwenden Sie die verfügbaren Informationen. Wenn nicht, überprüfen Sie, wie schnell Sie hämmern können, ohne erwischt zu werden, und verwenden Sie a sleep.
Torxed

Antworten:

155

Das Empfangen eines Status 429 ist kein Fehler . Der andere Server fordert Sie "freundlich" auf, die Spam-Anfragen zu beenden. Offensichtlich war Ihre Anforderungsrate zu hoch und der Server ist nicht bereit, dies zu akzeptieren.

Sie sollten nicht versuchen, dies "auszuweichen" oder sogar zu versuchen, die Server-Sicherheitseinstellungen zu umgehen, indem Sie versuchen, Ihre IP zu fälschen. Sie sollten einfach die Antwort des Servers respektieren, indem Sie nicht zu viele Anfragen senden.

Wenn alles richtig eingerichtet ist, haben Sie zusammen mit der 429-Antwort auch einen "Retry-after" -Header erhalten. Dieser Header gibt die Anzahl der Sekunden an, die Sie warten sollten, bevor Sie einen weiteren Anruf tätigen. Der richtige Weg, um mit diesem "Problem" umzugehen, besteht darin, diesen Header zu lesen und Ihren Prozess für so viele Sekunden auszuschalten.

Weitere Informationen zum Status 429 finden Sie hier: http://tools.ietf.org/html/rfc6585#page-3

MRA
quelle
23
Nun, niemand hat jemals gesagt, dass alle Webserver richtig konfiguriert sind. Da die meisten Ratenbegrenzer Besucher nach IP identifizieren, kann dies in einem Szenario, in dem IPs dynamisch gemeinsam genutzt werden, zu Problemen führen. Wenn Sie weiterhin den Status 429 erhalten, obwohl Sie sicher sind, dass Sie nicht zu viele Anfragen gesendet haben, können Sie sich an den Administrator der Site wenden.
MRA
1
Vielen Dank, dass Sie den Header "Retry-after" erwähnt haben. Ich würde ein Codebeispiel lieben, um zu sehen, wie man diesen Wert erhält (ich habe urllib verwendet, um OP zu mechanisieren, in beiden Fällen glaube ich nicht, dass die Header in der
ausgelösten
@MacFreek Ich habe keine bestimmten Python-Codebeispiele parat, aber ich gehe davon aus, dass einige Beispiele zum Abrufen von Antwortheadern
MRA
Danke @MRA. Ich habe festgestellt, dass die Header auch in der Ausnahme verfügbar sind: Nach dem Abfangen HTTPError as my_exceptionist sie my_exception.headerszumindest für urllib2 in verfügbar.
MacFreek
37

Das Schreiben dieses Codes hat mein Problem behoben:

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

tadm123
quelle
26
Diese Antwort wurde abgelehnt, aber einige Websites geben automatisch den Fehlercode 429 zurück, wenn der Benutzeragent aufgrund von Missbrauch durch andere Personen gesperrt wird. Wenn Sie den Fehlercode 429 erhalten, auch wenn Sie nur wenige Anfragen gesendet haben, versuchen Sie, den Benutzeragenten auf etwas anderes einzustellen.
Ferry Boender
7
Ich möchte auch hinzufügen, dass einige Websites Anfragen einfach ablehnen, es sei denn, ein Benutzeragent wird gesendet, und Sie erhalten möglicherweise eine Vielzahl anderer Antworten: 503/403 / eine allgemeine Indexseite.
user3791372
1
Kann dies bestätigen. Ich habe nur versucht, Python mit reddit zu verbinden und ohne den Benutzeragenten
einzustellen,
Können Sie bitte eine Erklärung hinzufügen?
Tokci
29

Wie MRA sagte, sollten Sie nicht versuchen, einem auszuweichen, 429 Too Many Requestssondern entsprechend damit umgehen. Abhängig von Ihrem Anwendungsfall haben Sie mehrere Möglichkeiten:

1) Schlaf deinen Prozess . Der Server enthält normalerweise einen Retry-afterHeader in der Antwort mit der Anzahl der Sekunden, die Sie warten sollen, bevor Sie es erneut versuchen. Denken Sie daran, dass das Schlafen eines Prozesses Probleme verursachen kann, z. B. in einer Aufgabenwarteschlange, in der Sie die Aufgabe stattdessen zu einem späteren Zeitpunkt wiederholen sollten, um den Mitarbeiter für andere Dinge freizugeben.

2) Exponentielles Backoff . Wenn der Server Ihnen nicht sagt, wie lange Sie warten sollen, können Sie Ihre Anfrage mit zunehmenden Pausen dazwischen wiederholen. In der beliebten Task-Warteschlange Sellerie ist diese Funktion direkt integriert .

3) Token-Eimer . Diese Technik ist nützlich, wenn Sie im Voraus wissen, wie viele Anfragen Sie in einer bestimmten Zeit stellen können. Jedes Mal, wenn Sie auf die API zugreifen, rufen Sie zuerst ein Token aus dem Bucket ab. Der Eimer wird mit konstanter Geschwindigkeit nachgefüllt. Wenn der Bucket leer ist, müssen Sie warten, bevor Sie die API erneut aufrufen. Token-Buckets werden normalerweise am anderen Ende (der API) implementiert. Sie können sie jedoch auch als Proxy verwenden, um zu vermeiden, dass Sie jemals einen erhalten 429 Too Many Requests. Die rate_limit- Funktion von Celery verwendet einen Token-Bucket-Algorithmus.

Hier ist ein Beispiel für eine Python / Sellerie-App mit exponentiellem Backoff und geschwindigkeitsbegrenzendem / 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
quelle
9

Eine andere Problemumgehung besteht darin, Ihre IP-Adresse mithilfe eines öffentlichen VPN- oder Tor-Netzwerks zu fälschen. Dies würde die Ratenbegrenzung auf dem Server auf IP-Ebene voraussetzen.

Es gibt einen kurzen Blog-Beitrag, der zeigt, wie man tor zusammen mit urllib2 verwendet:

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

Gaurav Agarwal
quelle
8
Aus diesem Grund müssen sich Benutzer meiner APIs immer für einen Schlüssel registrieren, um Anforderungen zu stellen. Auf diese Weise kann ich Anfragen eher nach Schlüssel als nach IP begrenzen. Die Registrierung für einen anderen Schlüssel wäre der einzige Weg, um ein höheres Limit zu erreichen.
Mnebuerquo