Jak uniknąć błędu HTTP 429 (zbyt wiele żądań) python

94

Próbuję użyć języka Python, aby zalogować się do witryny internetowej i zebrać informacje z kilku stron internetowych i otrzymuję następujący błąd:

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

Użyłem time.sleep()i działa, ale wydaje się nieinteligentny i zawodny, czy jest jakiś inny sposób na uniknięcie tego błędu?

Oto mój kod:

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
źródło
6
Nie da się tego obejść, jest to wymuszenie po stronie serwera, które śledzi liczbę żądań / jednostek czasu. Jeśli przekroczysz tę jednostkę, zostaniesz tymczasowo zablokowany. Niektóre serwery wysyłają te informacje w nagłówku, ale takie sytuacje są rzadkie. Sprawdź nagłówki otrzymane z serwera, skorzystaj z dostępnych informacji. Jeśli nie, sprawdź, jak szybko możesz młotkować, nie dając się złapać i użyj pliku sleep.
Torxed

Odpowiedzi:

158

Otrzymanie statusu 429 nie jest błędem , to jest inny serwer "uprzejmie" proszący o zaprzestanie spamowania żądań. Oczywiście liczba żądań była zbyt wysoka, a serwer nie chce tego zaakceptować.

Nie powinieneś próbować tego „omijać”, a nawet próbować ominąć ustawień bezpieczeństwa serwera, próbując sfałszować swój adres IP, powinieneś po prostu uszanować odpowiedź serwera, nie wysyłając zbyt wielu żądań.

Jeśli wszystko jest poprawnie skonfigurowane, otrzymasz także nagłówek „Ponów próbę po” wraz z odpowiedzią 429. Ten nagłówek określa liczbę sekund, które należy odczekać przed wykonaniem kolejnego połączenia. Właściwym sposobem radzenia sobie z tym „problemem” jest przeczytanie tego nagłówka i uśpienie procesu na tak wiele sekund.

Więcej informacji na temat statusu 429 można znaleźć tutaj: http://tools.ietf.org/html/rfc6585#page-3

MRA
źródło
23
Cóż, nikt nigdy nie powiedział, że wszystkie serwery internetowe są poprawnie skonfigurowane. Ponadto, ponieważ większość ograniczników szybkości identyfikuje odwiedzających na podstawie adresu IP, może to prowadzić do problemów w scenariuszu, w którym adresy IP są udostępniane dynamicznie. Jeśli nadal otrzymujesz status 429, mimo że masz pewność, że nie wysłałeś zbyt wielu żądań, możesz rozważyć skontaktowanie się z administratorem witryny.
MRA
2
Dziękujemy za wzmiankę o nagłówku „Ponów po”. Chciałbym przykład kodu, aby zobaczyć, jak uzyskać tę wartość (używałem urllib, aby OP mechanize, w każdym razie nie sądzę, nagłówki są wliczone w podniesionej wyjątku)
MacFreek
@MacFreek Nie mam gotowych żadnych konkretnych przykładów kodu w Pythonie, ale zakładam, że niektóre przykłady dotyczące pobierania nagłówków odpowiedzi w ogóle można zaczerpnąć z odpowiedzi na to pytanie: stackoverflow.com/q/843392
MRA
Dzięki @MRA. Okazało się, że nagłówki są również dostępne w wyjątku: po przechwyceniu HTTPError as my_exceptionjest dostępny w my_exception.headers, przynajmniej dla urllib2.
MacFreek
38

Napisanie tego fragmentu kodu rozwiązało mój problem:

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

tadm123
źródło
26
Ta odpowiedź jest odrzucana, ale niektóre witryny automatycznie zwracają kod błędu 429, jeśli klient użytkownika zostanie zbanowany z powodu nadużyć ze strony innych osób. Jeśli otrzymasz kod błędu 429, nawet jeśli wysłałeś tylko kilka żądań, spróbuj ustawić agenta użytkownika na coś innego.
Ferry Boender
7
Chciałbym również dodać, że niektóre witryny po prostu odrzucają żądania, chyba że wysłano klienta użytkownika, a możesz otrzymać mnóstwo innych odpowiedzi: 503/403 / jakaś ogólna strona indeksu.
user3791372
1
Mogę to potwierdzić. Po prostu próbowałem
połączyć
1
czy możesz dodać jakieś wyjaśnienie?
Tokci
Gdzie „piszesz ten fragment kodu”? To rozwiązanie wymaga więcej szczegółów.
Joe McLean
29

Jak powiedział MRA, nie powinieneś próbować unikać, 429 Too Many Requestsale zamiast tego odpowiednio sobie z tym poradzić. Masz kilka opcji w zależności od przypadku użycia:

1) Uśpij swój proces . Serwer zwykle zawiera Retry-afternagłówek w odpowiedzi z liczbą sekund, które należy odczekać przed ponowną próbą. Należy pamiętać, że uśpienie procesu może powodować problemy, np. W kolejce zadań, w której zamiast tego należy wykonać zadanie ponownie później, aby zwolnić pracownika do innych rzeczy.

2) Wykładnicze wycofanie . Jeśli serwer nie poinformuje Cię, jak długo masz czekać, możesz ponowić żądanie, używając coraz dłuższych przerw. Popularna kolejka zadań Seler ma tę funkcję wbudowaną bezpośrednio .

3) Wiadro z tokenami . Ta technika jest przydatna, jeśli wiesz z góry, ile żądań jesteś w stanie złożyć w danym czasie. Za każdym razem, gdy uzyskujesz dostęp do interfejsu API, najpierw pobierasz token z zasobnika. Wiaderko jest uzupełniane w stałym tempie. Jeśli zasobnik jest pusty, wiesz, że będziesz musiał poczekać przed ponownym naciśnięciem interfejsu API. Zasobniki tokenów są zwykle implementowane na drugim końcu (API), ale można ich również używać jako serwera proxy, aby uniknąć kiedykolwiek uzyskania pliku 429 Too Many Requests. Funkcja rate_limit selera używa algorytmu zasobnika tokenów.

Oto przykład aplikacji w języku Python / Celery używającej wykładniczego wycofywania i zasobnika ograniczającego szybkość / tokenów:

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
źródło
9

Innym obejściem byłoby sfałszowanie swojego adresu IP za pomocą jakiejś publicznej sieci VPN lub sieci Tor. Byłoby to przy założeniu ograniczenia szybkości na serwerze na poziomie IP.

Istnieje krótki post na blogu demonstrujący sposób używania tor wraz z urllib2:

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

Gaurav Agarwal
źródło
8
Dlatego zawsze wymagam, aby użytkownicy moich API zarejestrowali się w celu uzyskania klucza do wysyłania żądań. W ten sposób mogę ograniczyć żądania według klucza, a nie adresu IP. Rejestracja dla innego klucza byłaby jedynym sposobem uzyskania wyższego limitu.
Mnebuerquo
4
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
davidbrown
źródło
0

Znalazłem fajne obejście blokowania adresów IP podczas skrobania witryn. Pozwala uruchamiać Scrapera w nieskończoność, uruchamiając go z Google App Engine i automatycznie wdrażając ponownie, gdy otrzymasz 429.

Przeczytaj ten artykuł

Juan Luis Ruiz-tagle
źródło