如何避免 HTTP 错误 429(请求过多)python

Posted

技术标签:

【中文标题】如何避免 HTTP 错误 429(请求过多)python【英文标题】:How to avoid HTTP error 429 (Too Many Requests) python 【发布时间】:2014-05-12 05:42:35 【问题描述】:

我正在尝试使用 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")

【问题讨论】:

没有办法,这是服务器端的强制措施,用于跟踪您发出的请求数/时间单位。如果您超过此单位,您将被暂时阻止。一些服务器在标头中发送此信息,但这种情况很少见。检查从服务器收到的标头,使用可用的信息。如果没有,检查你可以多快敲击而不被抓住并使用sleep ***.com/questions/15648272/… 【参考方案1】:

另一种解决方法是使用某种公共 *** 或 Tor 网络来欺骗您的 IP。这将假设服务器在 IP 级别上的速率限制。

有一篇简短的博客文章展示了一种将 tor 与 urllib2 一起使用的方法:

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

【讨论】:

这就是为什么我总是要求我的 API 的用户注册一个密钥来提出请求。这样我可以通过键而不是 IP 来限制请求。注册另一个密钥将是获得更高限制的唯一方法。【参考方案2】:

收到状态 429 并不是错误,而是其他服务器“好心”地要求您停止发送垃圾邮件请求。很明显,你的请求率太高了,服务器不愿意接受。

你不应该试图“躲避”这一点,甚至试图通过欺骗你的 IP 来绕过服务器安全设置,你应该尊重服务器的回答,不要发送太多请求。

如果一切设置正确,您还会收到“Retry-after”标头以及 429 响应。此标头指定在进行另一个呼叫之前您应该等待的秒数。处理这个“问题”的正确方法是读取这个标题并让你的进程休眠几秒钟。

您可以在此处找到有关状态 429 的更多信息:https://www.rfc-editor.org/rfc/rfc6585#page-3

【讨论】:

好吧,没有人说过所有的 Web 服务器都配置正确。此外,由于大多数速率限制器都是通过 IP 识别访问者,这可能会在 IP 动态共享的情况下导致问题。如果您确信自己根本没有发送太多请求,但仍然收到状态 429,您可以考虑联系网站管理员。 感谢您提及“Retry-after”标头。我希望有一个代码示例来查看如何获取该值(我使用 urllib 来进行 OP 机械化,在任何一种情况下,我都不认为标头包含在引发的异常中) @MacFreek 我没有准备好任何特定的 Python 代码示例,但我假设一些关于如何检索响应标头的示例通常可以从这个问题的答案中获取:***.com/q/843392跨度> 谢谢@MRA。我发现异常中的标头也可用:捕获HTTPError as my_exception后,它在my_exception.headers中可用,至少对于urllib2。【参考方案3】:

在请求解决我的问题时编写这段代码:

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

这是因为网站有时会在未提供用户代理时返回太多请求 (429) 错误。例如,Reddit 的 API 仅在应用用户代理时才有效。

【讨论】:

这个答案被否决了,但是如果用户代理由于其他人的滥用而被禁止,一些网站会自动返回错误代码 429。如果即使您只发送了几个请求,您也收到错误代码 429,请尝试将用户代理设置为其他内容。 还想补充一点,除非发送用户代理,否则某些网站会明确拒绝请求,并且您可能会收到无数其他响应:503 / 403 / 一些通用索引页面。 可以确认这一点。只是试图将 python 与 reddit 接口并且没有设置用户代理,我总是收到错误代码 429。 你能补充一些解释吗? 你在哪里“写这段代码”?此解决方案需要更多详细信息。【参考方案4】:

正如 MRA 所说,您不应试图躲避 429 Too Many Requests,而应相应地处理它。根据您的用例,您有多种选择:

1) 让你的进程休眠。服务器通常在响应中包含一个Retry-after 标头,其中包含您在重试之前应该等待的秒数。请记住,休眠进程可能会导致问题,例如在任务队列中,您应该稍后重试该任务,以腾出工作人员做其他事情。

2) Exponential backoff。如果服务器没有告诉您等待多长时间,您可以使用增加的暂停来重试您的请求。流行的任务队列 Celery 有这个功能built right-in.

3) Token bucket。如果您事先知道在给定时间内可以发出多少请求,则此技术很有用。每次访问 API 时,您首先从存储桶中获取令牌。桶以恒定速率重新装满。如果存储桶是空的,您知道您必须等待才能再次访问 API。令牌桶通常在另一端(API)实现,但您也可以将它们用作代理以避免获得429 Too Many Requests。 Celery 的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()

【讨论】:

【参考方案5】:
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))

【讨论】:

简单实现的方式。 “Retry-After”可以是时间戳而不是秒数。见developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After【参考方案6】:

在抓取网站时,我发现了一个很好的IP 阻止解决方法。它可以让您无限期地运行 Scraper,方法是从 Google App Engine 运行它并在您收到 429 时自动重新部署它。

查看this article

【讨论】:

哈哈哇...用谷歌刮谷歌。然后在 Google 阻止时更改您的 Google IP。【参考方案7】:

在许多情况下,即使服务器要求您不要继续从网站上抓取数据,也是不道德的。但是,如果不是,您可以利用公共代理列表来抓取具有许多不同 IP 地址的网站。

【讨论】:

以上是关于如何避免 HTTP 错误 429(请求过多)python的主要内容,如果未能解决你的问题,请参考以下文章

负载测试中的 429 错误(Apache Jmeter)

urllib2 HTTP 错误 429

如何避免nodejs循环上的429

如何使用与 angular2 http 服务的保持连接

如何限制/速率限制请求以防止 Axios 出现 429 错误

避免ajax请求过多,导致内存溢出,请求之后回收资源