Python urllib2不遵守超时

use*_*497 4 urllib2 python-2.7

以下两行代码将永远挂起:

import urllib2
urllib2.urlopen('https://www.5giay.vn/', timeout=5)
Run Code Online (Sandbox Code Playgroud)

这是python2.7,并且没有设置http_proxy或任何其他env变量。任何其他网站都可以正常工作。我也可以毫无问题地获取该网站。可能是什么问题?

unu*_*tbu 5

如果你跑

import urllib2

url = 'https://www.5giay.vn/'
urllib2.urlopen(url, timeout=1.0)
Run Code Online (Sandbox Code Playgroud)

等待几秒钟,然后使用Cc中断程序,您将看到

  File "/usr/lib/python2.7/ssl.py", line 260, in read
    return self._sslobj.read(len)
KeyboardInterrupt
Run Code Online (Sandbox Code Playgroud)

这表明程序正在挂起 self._sslobj.read(len)

SSL超时增加 socket.timeout

您可以通过调用来控制socket.timeout引发之前的延迟 socket.setdefaulttimeout(1.0)

例如,

import urllib2
import socket

socket.setdefaulttimeout(1.0)
url = 'https://www.5giay.vn/'
try:
    urllib2.urlopen(url, timeout=1.0)
except IOError as err:
    print('timeout')
Run Code Online (Sandbox Code Playgroud)
% time script.py
timeout

real    0m3.629s
user    0m0.020s
sys 0m0.024s
Run Code Online (Sandbox Code Playgroud)

请注意,请求模块在此处成功,urllib2但未成功:

import requests
r = requests.get('https://www.5giay.vn/')
Run Code Online (Sandbox Code Playgroud)

如何对整个函数调用强制执行超时:

socket.setdefaulttimeout如果服务器未发出响应,则仅影响Python在引发异常之前等待的时间

它既urlopen(..., timeout=...)不对整个函数调用也没有实施时间限制。

为此,您可以使用事件变量,如下所示

如果您不想安装eventlets,则可以multiprocessing从标准库中使用;尽管此解决方案无法像异步解决方案(如eventlets提供的解决方案)那样扩展。

import urllib2
import socket
import multiprocessing as mp

def timeout(t, cmd, *args, **kwds):
    pool = mp.Pool(processes=1)
    result = pool.apply_async(cmd, args=args, kwds=kwds)
    try:
        retval = result.get(timeout=t)
    except mp.TimeoutError as err:
        pool.terminate()
        pool.join()
        raise
    else:
        return retval

def open(url):
    response = urllib2.urlopen(url)
    print(response)

url = 'https://www.5giay.vn/'
try:
    timeout(5, open, url)
except mp.TimeoutError as err:
    print('timeout')
Run Code Online (Sandbox Code Playgroud)

运行此命令将在大约5秒钟的挂钟时间中成功或超时。