Как избежать ошибки HTTP 429 (слишком много запросов) python

94

Я пытаюсь использовать 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")
Aous1000
источник
6
Обойти это невозможно, это принудительное применение на стороне сервера, отслеживающее, сколько запросов / единиц времени вы делаете. Если вы превысите эту единицу, вы будете временно заблокированы. Некоторые серверы отправляют эту информацию в заголовке, но такие случаи редки. Проверьте заголовки, полученные от сервера, используйте имеющуюся информацию. Если нет, проверьте, насколько быстро вы можете забивать, не попадаясь, и используйте файл sleep.
Torxed 01

Ответы:

158

Получение статуса 429 не является ошибкой , это другой сервер «любезно» просит вас прекратить рассылать спам-запросы. Очевидно, ваша скорость запросов была слишком высокой, и сервер не желает принимать это.

Вы не должны пытаться «уклоняться» от этого или даже пытаться обойти настройки безопасности сервера, пытаясь подделать ваш IP-адрес, вы должны просто уважать ответ сервера, не посылая слишком много запросов.

Если все настроено правильно, вы также получите заголовок «Retry-after» вместе с ответом 429. Этот заголовок определяет количество секунд, которое вы должны подождать, прежде чем сделать следующий звонок. Правильный способ справиться с этой «проблемой» - прочитать этот заголовок и приостановить ваш процесс на столько секунд.

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

MRA
источник
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'})

tadm123
источник
26
Этот ответ отвергнут, но некоторые сайты автоматически возвращают код ошибки 429, если пользовательский агент заблокирован из-за злоупотреблений со стороны других людей. Если вы получаете код ошибки 429, даже если вы отправили всего несколько запросов, попробуйте настроить пользовательский агент на что-то другое.
Ferry Boender 01
7
Также хотелось бы добавить, что некоторые сайты просто отклоняют запросы, если не отправлен пользовательский агент, и вы можете получить множество других ответов: 503/403 / некоторая общая индексная страница.
user3791372
1
Могу подтвердить это. Просто пытаясь связать python с Reddit и без настройки пользовательского агента, я всегда получал код ошибки 429.
Karrq
1
не могли бы вы добавить некоторые пояснения?
Tokci
Где вы «пишете этот кусок кода»? Это решение требует более подробной информации.
Джо Маклин,
29

Как сказал MRA, вы не должны пытаться увернуться от A, 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
4
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
Дэвидбраун
источник
1

Я нашел хороший способ обхода блокировки IP-адресов при парсинге сайтов. Он позволяет вам запускать Scraper на неопределенный срок, запустив его из Google App Engine и автоматически повторно развернув, когда вы получите 429.

Прочтите эту статью

Хуан Луис Руис-тагле
источник
Ха-ха ... используя Google для очистки Google. А затем изменить свой IP-адрес Google, когда Google его заблокирует.
sam1370