Come evitare l'errore HTTP 429 (Too Many Requests) python


91

Sto cercando di utilizzare Python per accedere a un sito Web e raccogliere informazioni da diverse pagine Web e ottengo il seguente errore:

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

L'ho usato time.sleep()e funziona, ma sembra poco intelligente e inaffidabile, c'è qualche altro modo per schivare questo errore?

Ecco il mio codice:

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")

6
Non c'è modo di aggirarlo, questa è un'applicazione sul lato server che tiene traccia di quante richieste / unità di tempo fai. Se superi questa unità sarai temporaneamente bloccato. Alcuni server inviano queste informazioni nell'intestazione, ma queste occasioni sono rare. Controlla le intestazioni ricevute dal server, utilizza le informazioni disponibili .. In caso contrario, controlla la velocità con cui puoi martellare senza essere catturato e usa un file sleep.
Torxed

Risposte:


158

Ricevere uno stato 429 non è un errore , è l'altro server che ti chiede "gentilmente" di interrompere le richieste di spamming. Ovviamente, il tuo tasso di richieste è stato troppo alto e il server non è disposto ad accettarlo.

Non dovresti cercare di "schivare" questo, o anche cercare di aggirare le impostazioni di sicurezza del server cercando di falsificare il tuo IP, dovresti semplicemente rispettare la risposta del server non inviando troppe richieste.

Se tutto è impostato correttamente, riceverai anche un'intestazione "Riprova dopo" insieme alla risposta 429. Questa intestazione specifica il numero di secondi da attendere prima di effettuare un'altra chiamata. Il modo corretto per affrontare questo "problema" è leggere questa intestazione e sospendere il processo per tanti secondi.

È possibile trovare ulteriori informazioni sullo stato 429 qui: http://tools.ietf.org/html/rfc6585#page-3


23
Ebbene, nessuno ha mai detto che tutti i server web siano configurati correttamente. Inoltre, poiché la maggior parte dei limitatori di velocità identifica i visitatori in base all'IP, ciò potrebbe portare a problemi in uno scenario in cui gli IP sono condivisi dinamicamente. Se continui a ricevere lo stato 429 anche se sei sicuro di non aver inviato troppe richieste, potresti prendere in considerazione l'idea di contattare l'amministratore del sito.
MRA

2
Grazie per aver menzionato l'intestazione "Riprova dopo". Mi piacerebbe un esempio di codice per vedere come ottenere quel valore (stavo usando urllib, per meccanizzare OP, in entrambi i casi non penso che le intestazioni siano incluse nell'eccezione sollevata)
MacFreek

@MacFreek Non ho alcun particolare esempio di codice Python pronto, ma presumo che alcuni esempi su come recuperare le intestazioni di risposta in generale possano essere presi dalle risposte a questa domanda: stackoverflow.com/q/843392
MRA

Grazie @MRA. Ho scoperto che le intestazioni sono disponibili anche nell'eccezione: dopo averlo catturato HTTPError as my_exception, è disponibile in my_exception.headers, almeno per urllib2.
MacFreek

37

Scrivere questo pezzo di codice ha risolto il mio problema:

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


26
Questa risposta è sottovalutata, ma alcuni siti restituiscono automaticamente il codice di errore 429 se l'agente utente viene bandito a causa di abusi da parte di altre persone. Se ricevi il codice di errore 429 anche se hai inviato solo poche richieste, prova a impostare l'agente utente su qualcos'altro.
Ferry Boender

7
Vorrei anche aggiungere che alcuni siti rifiutano chiaramente le richieste a meno che non venga inviato un agente utente, e potresti ottenere una miriade di altre risposte: 503/403 / qualche pagina di indice generica.
user3791372

1
Lo posso confermare. Cercavo solo di interfacciare python con reddit e senza impostare l'agente utente
ottenevo

puoi aggiungere qualche spiegazione per favore?
Tokci

29

Come ha detto MRA, non dovresti cercare di schivare un 429 Too Many Requestsma invece gestirlo di conseguenza. Hai diverse opzioni a seconda del tuo caso d'uso:

1) Dormi il tuo processo . Il server di solito include Retry-afterun'intestazione nella risposta con il numero di secondi che dovresti aspettare prima di riprovare. Tieni presente che la sospensione di un processo potrebbe causare problemi, ad esempio in una coda di attività, dove dovresti invece ritentare l'attività in un secondo momento per liberare il lavoratore per altre cose.

2) Backoff esponenziale . Se il server non ti dice quanto tempo aspettare, puoi riprovare la richiesta usando pause crescenti nel mezzo. La popolare coda di attività Celery ha questa funzione integrata direttamente .

3) Porta gettoni . Questa tecnica è utile se sai in anticipo quante richieste puoi fare in un dato momento. Ogni volta che accedi all'API, recuperi prima un token dal bucket. Il secchio viene riempito a una velocità costante. Se il bucket è vuoto, sai che dovrai aspettare prima di premere nuovamente l'API. I bucket di token sono solitamente implementati dall'altra parte (l'API) ma puoi anche usarli come proxy per evitare di ottenere un file 429 Too Many Requests. La funzione rate_limit di Celery utilizza un algoritmo di token bucket.

Ecco un esempio di un'app Python / Celery che utilizza il backoff esponenziale e il bucket di limitazione della velocità / token:

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()

9

Un'altra soluzione alternativa sarebbe falsificare il tuo IP usando una sorta di VPN pubblica o rete Tor. Ciò presupporrebbe la limitazione della velocità sul server a livello IP.

C'è un breve post sul blog che mostra un modo per usare tor insieme a urllib2:

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


8
Questo è il motivo per cui richiedo sempre agli utenti delle mie API di registrarsi per una chiave per effettuare richieste. In questo modo posso limitare le richieste per chiave piuttosto che per IP. La registrazione per un'altra chiave sarebbe l'unico modo per ottenere un limite più alto.
Mnebuerquo

2
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.