之前写了一个多线程爬取壁纸程序后,发现有时候运行起来,会有线程在打开某张图片的链接时卡住(似乎一般是在图片比较大的时候),报错也不报,许久都不能恢复。。
可以设置timeout参数来防止requests时间过长,并捕捉错误信息然后让再让其requests多几次:
try:
downloadPic(urlNum, self.threadID)
except Exception as e:
print('Thread %d:\tTry to request again #1' %(self.threadID))
# 请求超时,再试几次
requestOK = False # 标记
exceptionMs = str(e) # 先保存错误信息
for i in range(2):
try:
downloadPic(urlNum, self.threadID)
requestOK = True
break
except Exception as e:
print('Thread %d:\tTry to request again #%d' %(self.threadID, (i+2)))
# 请求三次还是不成功
if not requestOK:
downloadFail.append(picNames[urlNum]) # 记录下载失败的图片
print('Thread %d:\t%s' %(self.threadID, exceptionMs)) # 输出错误信息
但事实上,即使设置了timeout,线程仍有卡死的可能。
网上的一种做法是使用socket.setdefaulttimeout(seconds)来解决,但亲测并无卵用。
深究timeout的失效原因,发现是因为requests请求上设置的timeout判断的并不是整个请求的总时间,而是从与服务器连接成功后,客户端开始接受服务器的数据为计算起点的。
也就是说,若是requests请求停在了与服务器成功连接之前的步骤,那此时无论停了多久,都不会开始计算停留时间,所以永远都触发不到timeout。
那么与服务器连接前究竟是卡在了哪里呢?据网上说是卡在了DNS解析。
于是,我把DNS更改为阿里公共DNS(223.5.5.5 / 223.6.6.6)。
之后到目前是没再出现requests卡死的情况了!
所以总结一下:
一定要设置timeout,并使用可靠的DNS服务器地址。