J'essaie d'utiliser Python pour me connecter à un site Web et collecter des informations à partir de plusieurs pages Web. Le message d'erreur suivant s'affiche:
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
J'ai utilisé time.sleep()
et cela fonctionne, mais cela semble inintelligent et peu fiable. Existe-t-il un autre moyen d'esquiver cette erreur?
Voici mon 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")
Recevoir un statut 429 n'est pas une erreur , c'est l'autre serveur "aimablement" vous demandant de bien vouloir arrêter les demandes de spam. De toute évidence, votre taux de demandes est trop élevé et le serveur ne veut pas l’accepter.
Vous ne devez pas chercher à "esquiver" cela, ni même essayer de contourner les paramètres de sécurité du serveur en essayant d'usurper votre adresse IP, vous devez simplement respecter la réponse du serveur en n'envoyant pas trop de demandes.
Si tout est configuré correctement, vous aurez également reçu un en-tête "Réessayer après" avec la réponse 429. Cet en-tête indique le nombre de secondes d'attente avant de passer un autre appel. La bonne façon de traiter ce "problème" consiste à lire cet en-tête et à mettre votre processus en veille pendant plusieurs secondes.
Vous pouvez trouver plus d'informations sur le statut 429 ici: http://tools.ietf.org/html/rfc6585#page-
Écrire ce morceau de code a résolu mon problème:
requests.get(link, headers = {'User-agent': 'your bot 0.1'})
Comme MRA l'a dit, vous ne devriez pas essayer d'esquiver un 429 Too Many Requests
mais plutôt le manipuler en conséquence. Vous avez plusieurs options en fonction de votre cas d'utilisation:
1) Mettez en veille votre processus . Le serveur inclut généralement un en-tête Retry-after
dans la réponse, avec le nombre de secondes que vous êtes censé attendre avant de réessayer. N'oubliez pas que le fait de mettre en veille un processus peut poser des problèmes, par exemple dans une file d'attente de tâches, où vous devriez plutôt réessayer la tâche ultérieurement pour libérer le travailleur pour d'autres tâches.
2) délai exponentiel. Si le serveur ne vous dit pas combien de temps attendre, vous pouvez réessayer votre demande en utilisant des pauses croissantes. La file d'attente de tâches populaire Celery a cette fonctionnalité construit à droite .
3) compartiment à jetons. Cette technique est utile si vous connaissez à l'avance le nombre de demandes que vous pouvez faire dans un temps donné. Chaque fois que vous accédez à l'API, vous extrayez un jeton du compartiment. Le seau est rempli à un taux constant. Si le compartiment est vide, vous savez que vous devrez attendre avant de toucher à nouveau l'API. Les compartiments de jetons sont généralement implémentés à l'autre extrémité (l'API), mais vous pouvez également les utiliser comme proxy pour éviter de recevoir un 429 Too Many Requests
. La fonctionnalité rate_limit du céleri utilise un algorithme de compartiment à jetons.
Voici un exemple d'application Python/Celery utilisant un compartiment à jeton exponentiel et à limitation de débit:
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()
Une autre solution consiste à usurper votre adresse IP en utilisant une sorte de réseau public VPN ou Tor. Cela supposerait une limitation de débit sur le serveur au niveau IP.
Il y a un bref article de blog démontrant une façon d'utiliser tor avec urllib2: