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

158 投票
7 回答
454373 浏览
提问于 2025-04-18 00:50

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

7 个回答

8

另一种解决方法是通过使用公共VPN或Tor网络来伪装你的IP地址。这是因为服务器可能会根据IP地址来限制访问频率。

这里有一篇简短的博客文章,展示了如何将Tor与urllib2一起使用:

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

32

在编程中,有时候我们会遇到一些问题,特别是在使用某些工具或库的时候。这些问题可能会让我们感到困惑,但其实很多时候,解决这些问题的方法是相似的。我们可以通过查看别人遇到的类似问题和解决方案,来找到自己的答案。

比如,有人可能在使用某个编程语言时,发现代码运行不正常。他们会把这个问题发到一个叫StackOverflow的网站上,寻求帮助。在那里,其他程序员会分享他们的经验,告诉你可能是什么原因导致了这个问题,以及如何解决它。

总之,遇到问题时,不要害怕寻求帮助,很多人都经历过类似的情况,分享他们的解决方案可以帮助你更快地找到答案。

if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
49

正如MRA所说,你不应该试图绕过一个 429 请求过多 的错误,而是应该正确处理它。根据你的使用场景,你有几种选择:

1) 让你的程序“睡一觉”。服务器通常会在响应中包含一个 Retry-after 的头信息,告诉你需要等待多少秒再重试。要注意,让程序“睡觉”可能会引发一些问题,比如在任务队列中,你应该在稍后重试任务,以便让工作者可以处理其他事情。

2) 指数退避。如果服务器没有告诉你需要等待多久,你可以在重试请求时逐渐增加等待的时间。流行的任务队列Celery就有这个功能 内置

3) 令牌桶。如果你提前知道在特定时间内可以发送多少请求,这个方法就很有用。每次你访问API时,首先从桶里取一个令牌。这个桶会以固定的速度被重新填满。如果桶空了,你就知道需要等一等才能再次访问API。令牌桶通常是在API那一端实现的,但你也可以用它们作为代理,避免出现 429 请求过多 的错误。Celery的 速率限制功能使用了令牌桶算法。

下面是一个使用指数退避和速率限制/令牌桶的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()
64

写下这段代码后,我的问题就解决了:

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

之所以有效,是因为有些网站在没有提供用户代理的情况下,会返回“请求过多”(429)的错误。例如,Reddit的API只有在提供用户代理时才能正常工作。

229

收到状态码429并不是一个错误,这其实是另一个服务器在“好心”地告诉你,请停止发送过多请求。显然,你的请求频率太高了,服务器不愿意接受这么多请求。

你不应该试图“躲避”这个问题,或者通过伪装你的IP地址来绕过服务器的安全设置。你应该尊重服务器的回应,别发送太多请求。

如果一切设置得当,你还会收到一个“Retry-after”的头信息,和429的响应一起发来的。这个头信息会告诉你在再次发送请求之前需要等待多少秒。处理这个“问题”的正确方法是查看这个头信息,然后让你的程序暂停那么多秒。

你可以在这里找到关于状态码429的更多信息:https://www.rfc-editor.org/rfc/rfc6585#page-3

撰写回答