130

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

7 回答 7

194

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

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

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

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

于 2014-04-29T14:14:16.820 回答
56

在请求修复我的问题时编写这段代码:

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

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

于 2016-11-03T04:14:08.947 回答
40

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

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

2)指数退避。如果服务器没有告诉您等待多长时间,您可以使用增加的暂停来重试您的请求。流行的任务队列 Celery内置了这个功能。

3)令牌桶。如果您事先知道在给定时间内可以发出多少请求,则此技术很有用。每次访问 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()
于 2018-05-11T14:26:22.710 回答
19
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
于 2020-09-01T08:58:03.227 回答
9

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

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

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

于 2014-04-01T13:08:30.263 回答
7

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

看看这篇文章

于 2020-11-07T12:03:56.983 回答
1

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

于 2020-11-22T01:42:48.647 回答