我正在收集网站列表上的统计数据,为了简单起见,我正在使用请求。这是我的代码:
data=[]
websites=['http://google.com', 'http://bbc.co.uk']
for w in websites:
r= requests.get(w, verify=False)
data.append( (r.url, len(r.content), r.elapsed.total_seconds(), str([(l.status_code, l.url) for l in r.history]), str(r.headers.items()), str(r.cookies.items())) )
现在,我想要请求。10秒后进入超时,这样循环就不会卡住。
这个问题以前也很有趣,但没有一个答案是干净的。
我听说可能不使用请求是一个好主意,但我应该如何得到请求提供的好东西(元组中的那些)。
连接超时是请求等待客户端建立到远程机器的连接(对应于套接字上的connect()调用)的秒数。将连接超时设置为略大于3的倍数是一个很好的实践,3是默认的TCP数据包重传窗口。
一旦客户端连接到服务器并发送了HTTP请求,读超时就开始了。它是客户端等待服务器发送响应的秒数。(具体来说,它是客户端在从服务器发送字节之间等待的秒数。在99.9%的情况下,这是服务器发送第一个字节之前的时间)。
如果您为超时指定了一个值,则该超时值将应用于连接超时和读取超时。像下图:
r = requests.get('https://github.com', timeout=5)
如果你想分别设置connect和read的值,请指定一个元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
如果远程服务器非常慢,您可以告诉Requests永远等待响应,方法是将None作为超时值,然后检索一杯咖啡。
r = requests.get('https://github.com', timeout=None)
https://docs.python-requests.org/en/latest/user/advanced/#timeouts
连接超时是请求等待客户端建立到远程机器的连接(对应于套接字上的connect()调用)的秒数。将连接超时设置为略大于3的倍数是一个很好的实践,3是默认的TCP数据包重传窗口。
一旦客户端连接到服务器并发送了HTTP请求,读超时就开始了。它是客户端等待服务器发送响应的秒数。(具体来说,它是客户端在从服务器发送字节之间等待的秒数。在99.9%的情况下,这是服务器发送第一个字节之前的时间)。
如果您为超时指定了一个值,则该超时值将应用于连接超时和读取超时。像下图:
r = requests.get('https://github.com', timeout=5)
如果你想分别设置connect和read的值,请指定一个元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
如果远程服务器非常慢,您可以告诉Requests永远等待响应,方法是将None作为超时值,然后检索一杯咖啡。
r = requests.get('https://github.com', timeout=None)
https://docs.python-requests.org/en/latest/user/advanced/#timeouts
要创建超时,您可以使用信号。
解决这个案子最好的办法可能是
设置一个异常作为告警信号的处理程序
延迟十秒发出警报信号
在try-except-finally块中调用函数。
如果函数超时,则到达except块。
在finally块中,你中止了警报,所以它不会在以后发出信号。
下面是一些示例代码:
import signal
from time import sleep
class TimeoutException(Exception):
""" Simple Exception to be called on timeouts. """
pass
def _timeout(signum, frame):
""" Raise an TimeoutException.
This is intended for use as a signal handler.
The signum and frame arguments passed to this are ignored.
"""
# Raise TimeoutException with system default timeout message
raise TimeoutException()
# Set the handler for the SIGALRM signal:
signal.signal(signal.SIGALRM, _timeout)
# Send the SIGALRM signal in 10 seconds:
signal.alarm(10)
try:
# Do our code:
print('This will take 11 seconds...')
sleep(11)
print('done!')
except TimeoutException:
print('It timed out!')
finally:
# Abort the sending of the SIGALRM signal:
signal.alarm(0)
这里有一些注意事项:
它不是线程安全的,信号总是传递到主线程,所以你不能把它放在任何其他线程中。
在调度信号和执行实际代码之后会有一个轻微的延迟。这意味着示例即使只休眠了10秒也会超时。
但是,这些都在标准python库中!除了sleep函数导入,它只是一个导入。如果你要在很多地方使用超时,你可以很容易地把TimeoutException, _timeout和singaling放在一个函数中,然后调用它。或者你可以创建一个装饰器,并把它放在函数上,请看下面链接的答案。
你也可以将它设置为“上下文管理器”,这样你就可以在with语句中使用它:
import signal
class Timeout():
""" Timeout for use with the `with` statement. """
class TimeoutException(Exception):
""" Simple Exception to be called on timeouts. """
pass
def _timeout(signum, frame):
""" Raise an TimeoutException.
This is intended for use as a signal handler.
The signum and frame arguments passed to this are ignored.
"""
raise Timeout.TimeoutException()
def __init__(self, timeout=10):
self.timeout = timeout
signal.signal(signal.SIGALRM, Timeout._timeout)
def __enter__(self):
signal.alarm(self.timeout)
def __exit__(self, exc_type, exc_value, traceback):
signal.alarm(0)
return exc_type is Timeout.TimeoutException
# Demonstration:
from time import sleep
print('This is going to take maximum 10 seconds...')
with Timeout(10):
sleep(15)
print('No timeout?')
print('Done')
这种上下文管理器方法的一个可能的缺点是,您无法知道代码是否实际超时。
资料来源及推荐阅读:
关于信号的文档
这是@David Narayan对暂停的回答。他以装饰者的身份组织了上面的代码。