urllib2 超时但未关闭套接字连接
我正在制作一个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
的内容。这是一种快速的方法,可以用其他异常替换当前的异常。