Kia*_*ash 134 python timeout python-requests
我正在收集网站列表的统计数据,为了简单起见,我正在使用它的请求.这是我的代码:
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())) )
Run Code Online (Sandbox Code Playgroud)
现在,我希望requests.get在10秒后超时,这样循环就不会卡住.
这个问题之前也引起了人们的兴趣,但没有一个答案是清晰的.我将在此给予一些赏金以获得一个很好的答案.
我听说也许不使用请求是一个好主意,但那么我应该如何获得请求提供的好东西.(元组中的那些)
Luk*_*asa 224
设置超时参数:
r = requests.get(w, verify=False, timeout=10)
Run Code Online (Sandbox Code Playgroud)
只要您没有设置stream=True该请求,requests.get()如果连接超过十秒,或者服务器发送的数据超过十秒,这将导致调用超时.
Alv*_*aro 114
使用eventlet怎么样?如果您希望在10秒后超时请求,即使正在接收数据,此代码段也适用于您:
import requests
import eventlet
eventlet.monkey_patch()
with eventlet.Timeout(10):
requests.get("http://ipv4.download.thinkbroadband.com/1GB.zip", verify=False)
Run Code Online (Sandbox Code Playgroud)
Hie*_*ieu 75
更新:http://docs.python-requests.org/en/master/user/advanced/#timeouts
在新版本中requests:
如果为超时指定单个值,则如下所示:
r = requests.get('https://github.com', timeout=5)
Run Code Online (Sandbox Code Playgroud)
超时值将应用于超时connect和read超时.如果要单独设置值,请指定元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
Run Code Online (Sandbox Code Playgroud)
如果远程服务器非常慢,您可以通过传递None作为超时值然后检索一杯咖啡来告诉请求永远等待响应.
r = requests.get('https://github.com', timeout=None)
Run Code Online (Sandbox Code Playgroud)
我的旧(可能是过时的)答案(很久以前发布):
还有其他方法可以解决这个问题:
1.使用TimeoutSauce内部类
来自:https://github.com/kennethreitz/requests/issues/1928#issuecomment-35811896
Run Code Online (Sandbox Code Playgroud)import requests from requests.adapters import TimeoutSauce class MyTimeout(TimeoutSauce): def __init__(self, *args, **kwargs): connect = kwargs.get('connect', 5) read = kwargs.get('read', connect) super(MyTimeout, self).__init__(connect=connect, read=read) requests.adapters.TimeoutSauce = MyTimeout此代码应该使我们将读取超时设置为等于连接超时,这是您在Session.get()调用时传递的超时值.(注意,我实际上没有测试过这段代码,因此可能需要一些快速调试,我只是将它直接写入GitHub窗口.)
2.使用来自kevinburke的请求分组: https ://github.com/kevinburke/requests/tree/connect-timeout
从其文档:https://github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst
如果为超时指定单个值,则如下所示:
Run Code Online (Sandbox Code Playgroud)r = requests.get('https://github.com', timeout=5)超时值将应用于连接和读取超时.如果要单独设置值,请指定元组:
Run Code Online (Sandbox Code Playgroud)r = requests.get('https://github.com', timeout=(3.05, 27))
kevinburke已要求将其合并到主要请求项目中,但尚未被接受.
Tim*_*ado 38
import requests, sys, time
TOTAL_TIMEOUT = 10
def trace_function(frame, event, arg):
if time.time() - start > TOTAL_TIMEOUT:
raise Exception('Timed out!')
return trace_function
start = time.time()
sys.settrace(trace_function)
try:
res = requests.get('http://localhost:8080', timeout=(3, 6))
except:
raise
finally:
sys.settrace(None)
Run Code Online (Sandbox Code Playgroud)
尽管有所有答案,我相信该线程仍然缺乏适当的解决方案,并且没有现有答案提供合理的方法来完成应该简单明了的事情。
首先我们要说的是,到2023年,仍然绝对没有办法requests单独完成它。这是图书馆开发人员有意识的设计决定。
利用该参数的解决timeout方案根本无法完成他们想要做的事情。乍一看“似乎”有效这一事实纯粹是偶然的:
该timeout参数与请求的总执行时间完全无关。它仅控制底层套接字接收任何数据之前可以经过的最长时间。以 5 秒超时为例,服务器也可以每 4 秒发送 1 个字节的数据,这完全没问题,但不会对您有太大帮助。
stream带有和 的答案iter_content稍好一些,但它们仍然没有涵盖请求中的所有内容。从发送响应标头到发送响应标头之后,您实际上都没有收到任何内容iter_content,这属于同一问题 - 即使您使用 1 个字节作为 的块大小iter_content,读取完整的响应标头可能会花费完全任意的时间,并且您实际上永远无法到达从 读取任何响应正文的位置iter_content。
以下是一些完全打破基于timeoutandstream的方法的示例。全部尝试一下。无论您使用哪种方法,它们都会无限期地挂起。
服务器.py
import socket
import time
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
server.bind(('127.0.0.1', 8080))
server.listen()
while True:
try:
sock, addr = server.accept()
print('Connection from', addr)
sock.send(b'HTTP/1.1 200 OK\r\n')
# Send some garbage headers very slowly but steadily.
# Never actually complete the response.
while True:
sock.send(b'a')
time.sleep(1)
except:
pass
Run Code Online (Sandbox Code Playgroud)
演示1.py
import requests
requests.get('http://localhost:8080')
Run Code Online (Sandbox Code Playgroud)
演示2.py
import requests
requests.get('http://localhost:8080', timeout=5)
Run Code Online (Sandbox Code Playgroud)
演示3.py
import requests
requests.get('http://localhost:8080', timeout=(5, 5))
Run Code Online (Sandbox Code Playgroud)
演示4.py
import requests
with requests.get('http://localhost:8080', timeout=(5, 5), stream=True) as res:
for chunk in res.iter_content(1):
break
Run Code Online (Sandbox Code Playgroud)
我的方法是利用Python的sys.settrace函数。这非常简单。您不需要使用任何外部库或颠倒代码。与大多数其他答案不同,这实际上保证了代码在指定的时间内执行。请注意,您仍然需要指定timeout参数,因为settrace仅涉及 Python 代码。实际的套接字读取是外部系统调用,它们不包含在 中settrace,但包含在timeout参数中。因此,确切的时间限制不是TOTAL_TIMEOUT,而是一个在下面的注释中解释的值。
import requests
import sys
import time
# This function serves as a "hook" that executes for each Python statement
# down the road. There may be some performance penalty, but as downloading
# a webpage is mostly I/O bound, it's not going to be significant.
def trace_function(frame, event, arg):
if time.time() - start > TOTAL_TIMEOUT:
raise Exception('Timed out!') # Use whatever exception you consider appropriate.
return trace_function
# The following code will terminate at most after TOTAL_TIMEOUT + the highest
# value specified in `timeout` parameter of `requests.get`.
# In this case 10 + 6 = 16 seconds.
# For most cases though, it's gonna terminate no later than TOTAL_TIMEOUT.
TOTAL_TIMEOUT = 10
start = time.time()
sys.settrace(trace_function)
try:
res = requests.get('http://localhost:8080', timeout=(3, 6)) # Use whatever timeout values you consider appropriate.
except:
raise
finally:
sys.settrace(None) # Remove the time constraint and continue normally.
# Do something with the response
Run Code Online (Sandbox Code Playgroud)
就是这样!
Ped*_*ito 35
自2019年1月起,您可以使用timeout = int(seconds)参数requests >= 2.4.0,即:
requests.get(url, timeout=10)
Run Code Online (Sandbox Code Playgroud)
注意:
timeout不是整个响应下载的时间限制; 相反,如果服务器没有发出超时秒的响应(更准确地说,如果在底层套接字上没有收到超时秒的字节),则会引发异常.如果未明确指定超时,则请求不会超时.
tot*_*aka 23
要创建超时,您可以使用信号.
解决这种情况的最佳方法可能是
try-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)
Run Code Online (Sandbox Code Playgroud)
这有一些警告:
但是,它都在标准的python库中!除了睡眠功能导入外,它只有一个导入.如果你打算在许多地方使用超时你可以轻松地将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')
Run Code Online (Sandbox Code Playgroud)
使用此上下文管理器方法的一个可能的缺点是您无法知道代码是否实际超时.
来源和推荐阅读:
Ale*_*hen 15
连接超时是指number of seconds请求将等待您的客户端在套接字上调用与远程计算机建立连接(对应于 connect())。将连接超时设置为略大于 3 的倍数是一个很好的做法,这是默认的 TCP 数据包重传窗口。
一旦客户端连接到服务器并发送 HTTP 请求,读取超时就开始了。这是客户端等待服务器发送响应的秒数。(具体来说,它是客户端在服务器发送字节之间等待的秒数。在 99.9% 的情况下,这是服务器发送第一个字节之前的时间)。
\n如果为超时指定单个值,则该超时值将应用于连接超时和读取超时。像下面这样:
\nr = requests.get(\'https://github.com\', timeout=5)\nRun Code Online (Sandbox Code Playgroud)\n如果您想单独设置连接和读取的值,请指定一个元组:
\nr = requests.get(\'https://github.com\', timeout=(3.05, 27))\nRun Code Online (Sandbox Code Playgroud)\n如果远程服务器非常慢,您可以通过传递 None 作为超时值来告诉 Requests 永远等待响应,然后检索一杯咖啡。
\nr = requests.get(\'https://github.com\', timeout=None)\nRun Code Online (Sandbox Code Playgroud)\nhttps://docs.python-requests.org/en/latest/user/advanced/#timeouts
\n设置stream=True和使用r.iter_content(1024). 是的,eventlet.Timeout只是不知何故对我不起作用。
try:
start = time()
timeout = 5
with get(config['source']['online'], stream=True, timeout=timeout) as r:
r.raise_for_status()
content = bytes()
content_gen = r.iter_content(1024)
while True:
if time()-start > timeout:
raise TimeoutError('Time out! ({} seconds)'.format(timeout))
try:
content += next(content_gen)
except StopIteration:
break
data = content.decode().split('\n')
if len(data) in [0, 1]:
raise ValueError('Bad requests data')
except (exceptions.RequestException, ValueError, IndexError, KeyboardInterrupt,
TimeoutError) as e:
print(e)
with open(config['source']['local']) as f:
data = [line.strip() for line in f.readlines()]
Run Code Online (Sandbox Code Playgroud)
尝试使用超时和错误处理此请求:
import requests
try:
url = "http://google.com"
r = requests.get(url, timeout=10)
except requests.exceptions.Timeout as e:
print e
Run Code Online (Sandbox Code Playgroud)
这可能有点矫枉过正,但 Celery 分布式任务队列对超时有很好的支持。
特别是,您可以定义一个软时间限制,它只会在您的流程中引发异常(以便您可以清理)和/或一个硬时间限制,在超过时间限制时终止任务。
在幕后,这使用了与您的“之前”帖子中提到的相同的信号方法,但以更有用和更易于管理的方式。如果您正在监视的网站列表很长,您可能会受益于它的主要功能——管理大量任务执行的各种方法。
我相信您可以使用multiprocessing而不依赖于第三方软件包:
import multiprocessing
import requests
def call_with_timeout(func, args, kwargs, timeout):
manager = multiprocessing.Manager()
return_dict = manager.dict()
# define a wrapper of `return_dict` to store the result.
def function(return_dict):
return_dict['value'] = func(*args, **kwargs)
p = multiprocessing.Process(target=function, args=(return_dict,))
p.start()
# Force a max. `timeout` or wait for the process to finish
p.join(timeout)
# If thread is still active, it didn't finish: raise TimeoutError
if p.is_alive():
p.terminate()
p.join()
raise TimeoutError
else:
return return_dict['value']
call_with_timeout(requests.get, args=(url,), kwargs={'timeout': 10}, timeout=60)
Run Code Online (Sandbox Code Playgroud)
传递给的超时是从服务器获取任何kwargs响应的超时,参数是获取完整响应的超时。timeout
| 归档时间: |
|
| 查看次数: |
178063 次 |
| 最近记录: |