urllib2 超时但未关闭套接字连接

0 投票
2 回答
1571 浏览
提问于 2025-04-15 18:28

我正在制作一个Python的URL抓取程序。为了我的需求,我希望它能非常快速地超时,所以我使用了

urllib2.urlopen("http://.../", timeout=2)

当然,它会按预期超时。但是,它并没有关闭与服务器的连接,所以服务器认为客户端仍然是连接状态。我该如何让urllib2在超时后关闭连接呢?

运行gc.collect()并没有效果,而且如果可以的话,我不想使用httplib。

我能做到的最接近的情况是:第一次尝试会超时。服务器报告连接在第二次尝试超时时刚好关闭。然后,服务器在第三次尝试超时时刚好报告连接关闭。如此循环。

非常感谢。

2 个回答

0

这真是个小技巧,但下面的代码确实有效。如果请求是在另一个函数里,并且没有出现异常,那么这个连接总是会被关闭。

def _fetch(self, url):
    try:
        return urllib2.urlopen(urllib2.Request(url), timeout=5).read()
    except urllib2.URLError, e:
        if isinstance(e.reason, socket.timeout):
            return None
        else:
            raise e

def fetch(self, url):
    x = None
    while x is None:
        x = self._fetch(url)
        print "Timeout"
    return x

有没有人有更好的方法呢?

2

我怀疑在调用栈中,那个socket连接还没有关闭。当Python抛出异常时,它会保存调用栈的信息,这样调试工具和其他工具就可以查看这个栈以及里面的值。

出于历史原因,以及为了向后兼容,栈的信息是按线程存储在sys模块里的(可以查看sys.exc_info()、sys.exc_type等)。这也是Python 3.0中移除的一项功能。

这对你来说意味着,调用栈仍然存在,并且被引用着。这个栈里保存着某个函数的局部数据,而这个函数正好有一个打开的socket。这就是为什么socket还没有关闭的原因。只有当调用栈被移除后,所有的东西才会被垃圾回收。

要测试是否真是这样,可以在你的异常处理代码中插入类似

try:
  1/0
except ZeroDivisionError:
  pass

的内容。这是一种快速的方法,可以用其他异常替换当前的异常。

撰写回答