如何在urllib2中超时后重试加载页面?

2 投票
3 回答
4488 浏览
提问于 2025-04-18 17:06

我想让Python在遇到超时错误时重新加载页面。有没有办法让我设置它重试特定的次数,可能还要在特定的时间间隔后再重试?

任何帮助都非常感谢。

谢谢。

3 个回答

0

可以看看这个requests库。如果你只想等一段指定的时间(不是等整个下载完成,而是等服务器给你回应),只需要在标准的URL请求中加上timeout参数,单位是秒:

r = requests.get(url, timeout=10)

如果超过了timeout设定的时间,就会抛出一个requests.exceptions.Timeout异常,你可以根据自己的需要来处理这个异常。比如,你可以把请求放在一个try/except块里,如果抛出异常,就捕获它,并在指定的次数内重新尝试连接,直到彻底失败。

你可能还想了解一下requests.adapters.HTTPAdapter,它有一个max_retries参数。这个通常是在Requests的Session中使用,根据文档,它提供了一个通用的接口,让Requests会话能够通过实现传输适配器接口来联系HTTP和HTTPS网址。

0

虽然我也是刚接触Python,但我觉得像这样的简单解决方案也许能解决问题。

首先可以把一些东西看作是None,也就是空值,这里的“东西”指的是页面源代码(page_source)。另外,我只考虑了URLError这个错误类型,你可以根据需要添加更多的错误处理。

import urllib2
import time
stuff=None
max_attempts=4
r=0
while stuff is None and r<max_attempts:
    try:
        response = urllib2.urlopen('http://www.google.com/ncr', timeout=10)
        stuff = response.read()
    except urllib2.URLError:
        r=r+1
        print "Re-trying, attempt -- ",r
        time.sleep(5)
        pass
print stuff

希望这能帮到你。

祝好,

Md. Mohsin

4

urllib2 里没有现成的功能来处理这个问题,但你可以自己写一个。

比较棘手的是,正如urlopen的文档所说,无论发生什么错误,你得到的都是一个 URLError。那么,怎么知道是超时了,还是其他问题呢?

如果你查一下URLError,会发现它有一个 reason,对于远程网址来说,这个 reasonsocket.error。再查一下socket.error,你会看到它是 IOErrorOSError 的子类(具体取决于你的 Python 版本)。再查一下OSError,你会发现它有一个 errno,用来表示底层的错误。

那么,超时的 errno 值是什么呢?我敢打赌是 EINPROGRESS,但我们来确认一下:

>>> urllib.urlopen('http://127.0.0.1', timeout=0)
urllib2.URLError: <urlopen error [Errno 36] Operation now in progress>
>>> errno.errorcode[36]
'EINPROGRESS'

(你可以直接用数字 36,但这个数字在不同平台上不一定相同;使用 errno.EINPROGRESS 会更通用。)

所以:

import errno
import urllib2

def retrying_urlopen(retries, *args, **kwargs):
    for i in range(retries):
        try:
            return urllib2.urlopen(*args, **kwargs)
        except URLError as e:
            if e.reason.errno == errno.EINPROGRESS:
                continue
            raise

如果你觉得这个过程很麻烦,应该更简单一些……我想大家都同意这一点。异常处理已经经历了两次大幅改进,接下来还会有一次大的改进,以及一些小的变化。但如果你还在用 2.7 版本,就享受不到这些改进的好处。

如果不能升级到 Python 3.4,也许可以考虑使用像 requestsurllib3 这样的第三方模块。这两个库都有专门处理 Timeout 的异常类型,而不是让你去处理一个通用的 URLError 的细节。

撰写回答