Як уникнути помилки HTTP 429 (забагато запитів) python


93

Я намагаюся використовувати Python для входу на веб-сайт та збору інформації з кількох веб-сторінок, і я отримую таку помилку:

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

Я використовував, time.sleep()і це працює, але це здається неінтелектуальним і ненадійним, чи є інший спосіб уникнути цієї помилки?

Ось мій код:

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
Це ніяк не можна обійти, це примусове забезпечення на стороні сервера, яке відстежує, скільки запитів / одиниць часу ви робите. Якщо ви перевищите цей показник, вас тимчасово заблокують. Деякі сервери надсилають цю інформацію в шапці, але такі випадки трапляються рідко. Перевірте заголовки, отримані з сервера, скористайтеся доступною інформацією .. Якщо ні, перевірте, наскільки швидко ви можете забивати, не потрапляючи і використовуйте a sleep.
Torxed

Відповіді:


158

Отримання статусу 429 не є помилкою , це інший сервер, що “люб’язно” просить вас припинити запити на спам. Очевидно, що ваш рівень запитів був занадто високим, і сервер не бажає приймати це.

Вам не слід намагатися "ухилитися" від цього або навіть намагатися обійти налаштування безпеки сервера, намагаючись підробити ваш IP, ви повинні просто поважати відповідь сервера, не надсилаючи занадто багато запитів.

Якщо все налаштовано належним чином, ви також отримаєте заголовок "Повторити спробу" разом із відповіддю 429. Цей заголовок визначає кількість секунд, яку слід зачекати, перш ніж здійснити ще один дзвінок. Правильний спосіб вирішити цю "проблему" - прочитати цей заголовок і затримати процес протягом багатьох секунд.

Більше інформації про статус 429 можна знайти тут: http://tools.ietf.org/html/rfc6585#page-3


23
Ну, ніхто ніколи не говорив, що всі веб-сервери налаштовані правильно. Крім того, оскільки більшість обмежувачів ставок ідентифікують відвідувачів за IP, це може призвести до проблем у сценарії, коли IP-адреси діляться динамічно. Якщо ви продовжуєте отримувати статус 429, хоча ви впевнені, що взагалі не надсилали занадто багато запитів, ви можете звернутися до адміністратора сайту.
MRA

2
Дякуємо, що згадали заголовок "Повторити спробу". Я хотів би приклад коду, щоб побачити, як отримати це значення (я використовував urllib, щоб механізувати OP, в будь-якому випадку я не думаю, що заголовки включені у піднятий виняток)
MacFreek,

@MacFreek У мене немає якихось конкретних прикладів коду Python, але я припускаю, що деякі приклади про те, як загалом отримати заголовки відповідей, можна взяти з відповідей на це питання: stackoverflow.com/q/843392
MRA

Дякую @MRA. Я виявив, що заголовки доступні і у винятку: після лову HTTPError as my_exceptionвін доступний у my_exception.headers, принаймні для urllib2.
MacFreek

38

Написання цього фрагмента коду вирішило мою проблему:

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


26
Ця відповідь проти, але деякі сайти автоматично повертають код помилки 429, якщо агент користувача заборонений через зловживання з боку інших людей. Якщо ви отримуєте код помилки 429, навіть якщо ви надіслали лише декілька запитів, спробуйте встановити для агента користувача щось інше.
Ferry Boender 01.03.17

7
Також хотілося б додати, що деякі сайти явно відмовляють у запитах, якщо не надіслано агент користувача, і ви можете отримати безліч інших відповідей: 503/403 / якась загальна сторінка покажчика.
user3791372

1
Можна підтвердити це. Просто намагаючись взаємодіяти python з reddit і не встановлюючи користувальницького агента, я завжди отримував код помилки 429.
Karrq

1
Ви можете додати пояснення, будь ласка?
Токчі

Де ви "пишете цей шматок коду"? Це рішення потребує деталей.
Джо Маклін

29

Як сказав MRA, не слід намагатися ухилятися від а, 429 Too Many Requestsа натомість обробляти це відповідно. У вас є кілька варіантів, залежно від вашого варіанту використання:

1) Спіть свій процес . Сервер зазвичай включає Retry-afterу відповідь заголовок із кількістю секунд, яку ви повинні зачекати перед повторною спробою. Майте на увазі, що сплячий процес може спричинити проблеми, наприклад, у черзі завдань, де замість цього слід пізніше повторити завдання, щоб звільнити працівника для інших речей.

2) Експоненціальне зниження . Якщо сервер не повідомляє вам, як довго чекати, ви можете повторити свій запит, використовуючи посилення пауз між ними. У популярній черзі завдань Celery вбудовано цю функцію .

3) Відро жетонів . Цей прийом корисний, якщо ви заздалегідь знаєте, скільки запитів ви можете зробити за певний час. Кожного разу, коли ви отримуєте доступ до API, ви спочатку отримуєте маркер із сегмента. Відро наповнюється постійною швидкістю. Якщо відро порожнє, ви знаєте, що вам доведеться почекати, перш ніж знову натискати API. Маркери сегментів зазвичай реалізуються на іншому кінці (API), але ви також можете використовувати їх як проксі-сервер, щоб уникнути коли-небудь отримання 429 Too Many Requests. Функція rate_limit у селері використовує алгоритм сегмента маркера.

Ось приклад програми Python / Celery з використанням експоненціальної відмови та обмеження швидкості / сегмента маркерів:

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

Іншим обхідним шляхом є підробка вашого IP за допомогою якоїсь загальнодоступної мережі VPN або Tor. Це було б припускати обмеження швидкості на сервері на рівні IP.

Існує коротка публікація в блозі, яка демонструє спосіб використання tor разом з urllib2:

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


8
Ось чому я завжди вимагаю від користувачів мого API реєстрації ключа для надсилання запитів. Таким чином я можу обмежувати запити за ключем, а не за IP. Реєстрація за іншим ключем буде єдиним способом отримати вищий ліміт.
Mnebuerquo

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.