python requests.get超时的完成响应

时间:2014-02-23 07:36:21

标签: 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())) )

现在,我想让requests.get在10秒后超时,这样循环就不会卡住。

这个问题也引起了人们的兴趣before,但没有一个答案是清晰的。我会在这方面给予一些赏金以获得一个好的答案。

我听说也许不使用请求是一个好主意,但那么我应该如何获得请求提供的好东西。 (元组中的那些)

21 个答案:

答案 0 :(得分:156)

设置timeout parameter

r = requests.get(w, verify=False, timeout=10)

只要您没有在该请求上设置stream=True,如果连接超过十秒,或者服务器未发送,这将导致对requests.get()的调用超时数据超过十秒钟。

答案 1 :(得分:105)

使用eventlet怎么样?如果您想在10秒后超时请求,即使正在接收数据,此代码段也适用于您:

import requests
import eventlet
eventlet.monkey_patch()

with eventlet.Timeout(10):
    requests.get("http://ipv4.download.thinkbroadband.com/1GB.zip", verify=False)

答案 2 :(得分:60)

更新:http://docs.python-requests.org/en/master/user/advanced/#timeouts

新版requests

如果为超时指定单个值,请执行以下操作:

r = requests.get('https://github.com', timeout=5)

超时值将同时应用于connectread超时。如果要单独设置值,请指定元组:

r = requests.get('https://github.com', timeout=(3.05, 27))

如果远程服务器非常慢,您可以通过将None作为超时值传递然后检索一杯咖啡来告诉请求永远等待响应。

r = requests.get('https://github.com', timeout=None)

我的旧(可能是过时的)答案(很久以前发布):

还有其他方法可以解决这个问题:

<强> 1。使用TimeoutSauce内部课程

来自:https://github.com/kennethreitz/requests/issues/1928#issuecomment-35811896

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
     

此代码应该使我们将读取超时设置为等于   connect timeout,这是你传递的超时值   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

  

如果为超时指定单个值,请执行以下操作:

r = requests.get('https://github.com', timeout=5)
     

超时值将应用于连接和读取   超时。如果要设置值,请指定元组   单独:

r = requests.get('https://github.com', timeout=(3.05, 27))

kevinburke has requested它将合并到主要请求项目中,但尚未被接受。

答案 3 :(得分:25)

timeout = int(seconds)

requests >= 2.4.0起,您可以使用timeoutrequests自变量,即:

requests.get(url, timeout=10)

注意:

  

timeout不是整个响应下载的时间限制;而是   如果服务器尚未针对以下内容发出响应,则会引发exception   超时秒数(更确切地说,如果在   基础套接字超时秒)。如果未指定超时   明确地,请求不会超时。

答案 4 :(得分:22)

要创建超时,您可以使用signals

解决此案例的最佳方法可能是

  1. 将异常设置为警报信号的处理程序
  2. 以十秒延迟调用警报信号
  3. try-except-finally区块内调用该功能。
  4. 如果函数超时,则到达except块。
  5. 在最后一个阻止中,你会中止警报,所以以后不会单独调整。
  6. 以下是一些示例代码:

    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)
    

    有一些警告:

    1. 它不是线程安全的,信号总是传递给主线程,所以你不能把它放在任何其他线程中。
    2. 在调度信号和执行实际代码之后有一点延迟。这意味着即使它只睡了十秒钟,这个例子也会超时。
    3. 但是,它都在标准的python库中!除了睡眠功能导入外,它只有一个导入。如果你打算在很多地方使用超时你可以轻松地将TimeoutException,_timeout和singaling放在一个函数中,然后调用它。或者您可以制作装饰器并将其放在功能上,请参阅下面链接的答案。

      您也可以将其设置为"context manager",以便将其与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')
      

      使用此上下文管理器方法的一个可能的缺点是您无法知道代码是否实际超时。

      来源和推荐阅读:

答案 5 :(得分:4)

这可能有些过分,但Celery分布式任务队列对超时有很好的支持。

特别是,您可以定义一个软时间限制,它只会在您的进程中引发异常(因此您可以清理)和/或在超出时间限制时终止任务的硬时限。

在封面下,这使用了与“之前”帖子中引用的相同的信号方法,但是以更加实用和可管理的方式。如果您监控的网站列表很长,您可能会受益于其主要功能 - 管理大量任务执行的各种方法。

答案 6 :(得分:3)

连接超时 是请求将等待您的客户端建立到远程机器的连接(对应于套接字上的 connect())调用的秒数。将连接超时设置为略大于 3 的倍数是一个很好的做法,这是默认的 TCP 数据包重传窗口。

一旦您的客户端连接到服务器并发送了 HTTP 请求,读取超时就会开始。它是客户端等待服务器发送响应的秒数。 (具体来说,它是客户端在从服务器发送的字节之间等待的秒数。在 99.9% 的情况下,这是服务器发送第一个字节之前的时间)。

如果为超时指定单个值,如下所示:

r = requests.get('https://github.com', timeout=5)

超时值将应用于连接和读取超时。如果您想单独设置值,请指定一个元组:

r = requests.get('https://github.com', timeout=(3.05, 27))

如果远程服务器很慢,您可以通过传递 None 作为超时值然后检索一杯咖啡来告诉请求永远等待响应。

r = requests.get('https://github.com', timeout=None)

https://python-requests.org/en/master/user/advanced/#id16

答案 7 :(得分:3)

请原谅,但我想知道为什么没有人提出以下更简单的解决方案? :-o

## request
requests.get('www.mypage.com', timeout=20)

答案 8 :(得分:3)

我相信您可以使用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)

传递给kwargs的超时是从服务器获取任何响应的超时,参数timeout是获取完整的超时时间>回应。

答案 9 :(得分:1)

尽管有关于请求的问题,但我发现使用pycurl CURLOPT_TIMEOUT或CURLOPT_TIMEOUT_MS非常容易。

无需线程或信令:

import pycurl
import StringIO

url = 'http://www.example.com/example.zip'
timeout_ms = 1000
raw = StringIO.StringIO()
c = pycurl.Curl()
c.setopt(pycurl.TIMEOUT_MS, timeout_ms)  # total timeout in milliseconds
c.setopt(pycurl.WRITEFUNCTION, raw.write)
c.setopt(pycurl.NOSIGNAL, 1)
c.setopt(pycurl.URL, url)
c.setopt(pycurl.HTTPGET, 1)
try:
    c.perform()
except pycurl.error:
    traceback.print_exc() # error generated on timeout
    pass # or just pass if you don't want to print the error

答案 10 :(得分:1)

timeout =(连接超时,数据读取超时)或给出一个参数(timeout = 1)

import requests

try:
    req = requests.request('GET', 'https://www.google.com',timeout=(1,1))
    print(req)
except requests.ReadTimeout:
    print("READ TIME OUT")

答案 11 :(得分:1)

此代码适用于socketError 11004和10060 ......

# -*- encoding:UTF-8 -*-
__author__ = 'ACE'
import requests
from PyQt4.QtCore import *
from PyQt4.QtGui import *


class TimeOutModel(QThread):
    Existed = pyqtSignal(bool)
    TimeOut = pyqtSignal()

    def __init__(self, fun, timeout=500, parent=None):
        """
        @param fun: function or lambda
        @param timeout: ms
        """
        super(TimeOutModel, self).__init__(parent)
        self.fun = fun

        self.timeer = QTimer(self)
        self.timeer.setInterval(timeout)
        self.timeer.timeout.connect(self.time_timeout)
        self.Existed.connect(self.timeer.stop)
        self.timeer.start()

        self.setTerminationEnabled(True)

    def time_timeout(self):
        self.timeer.stop()
        self.TimeOut.emit()
        self.quit()
        self.terminate()

    def run(self):
        self.fun()


bb = lambda: requests.get("http://ipv4.download.thinkbroadband.com/1GB.zip")

a = QApplication([])

z = TimeOutModel(bb, 500)
print 'timeout'

a.exec_()

答案 12 :(得分:0)

好吧,我在这个页面上尝试了很多解决方案,但仍然面临不稳定,随机挂起,连接性能不佳。

我现在正在使用Curl,我真的很高兴它的“最大时间”功能和全球表现,即使实施得如此糟糕:

content=commands.getoutput('curl -m6 -Ss "http://mywebsite.xyz"')

在这里,我定义了一个6秒的最大时间参数,同时兼顾连接和传输时间。

我确定Curl有一个很好的python绑定,如果你更喜欢坚持pythonic语法:)

答案 13 :(得分:0)

设置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()]

讨论在https://redd.it/80kp1h

答案 14 :(得分:0)

如果您使用stream=True选项,则可以执行此操作:

r = requests.get(
    'http://url_to_large_file',
    timeout=1,  # relevant only for underlying socket
    stream=True)

with open('/tmp/out_file.txt'), 'wb') as f:
    start_time = time.time()
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:  # filter out keep-alive new chunks
            f.write(chunk)
        if time.time() - start_time > 8:
            raise Exception('Request took longer than 8s')

该解决方案不需要信号或多处理。

答案 15 :(得分:0)

又一个解决方案(从http://docs.python-requests.org/en/master/user/advanced/#streaming-uploads获得了解决方案)

上传之前,您可以确定内容大小:

TOO_LONG = 10*1024*1024  # 10 Mb
big_url = "http://ipv4.download.thinkbroadband.com/1GB.zip"
r = requests.get(big_url, stream=True)
print (r.headers['content-length'])
# 1073741824  

if int(r.headers['content-length']) < TOO_LONG:
    # upload content:
    content = r.content

但是请注意,发件人可以在“内容长度”响应字段中设置错误的值。

答案 16 :(得分:0)

有一个名为timeout-decorator的软件包,可用于使任何python函数超时。

@timeout_decorator.timeout(5)
def mytest():
    print("Start")
    for i in range(1,10):
        time.sleep(1)
        print("{} seconds have passed".format(i))

它使用一些此处建议的信号方法。另外,您可以告诉它使用多处理而不是信号(例如,如果您处于多线程环境中)。

答案 17 :(得分:0)

尝试此请求并进行超时和错误处理:

import requests
try: 
    url = "http://google.com"
    r = requests.get(url, timeout=10)
except requests.exceptions.Timeout as e: 
    print e

答案 18 :(得分:0)

我正在使用请求2.2.1,eventlet不适用于我。相反,我可以使用gevent超时,因为在我的服务中,gunicorn使用了gevent。

import gevent
import gevent.monkey
gevent.monkey.patch_all(subprocess=True)
try:
    with gevent.Timeout(5):
        ret = requests.get(url)
        print ret.status_code, ret.content
except gevent.timeout.Timeout as e:
    print "timeout: {}".format(e.message)

请注意,一般的异常处理不会捕获gevent.timeout.Timeout。 因此,要么显式捕获gevent.timeout.Timeout 或传递不同的异常以供使用,例如:with gevent.Timeout(5, requests.exceptions.Timeout):,但引发此异常时不会传递任何消息。

答案 19 :(得分:0)

如果是这样,请创建一个 watchdog 线程,在10秒后混淆请求的内部状态,例如:

  • 关闭底层套接字,理想情况下
  • 如果请求重试操作,
  • 会触发异常

请注意,根据系统库,您可能无法设置DNS解析的截止日期。

答案 20 :(得分:-1)

我提出了一个更直接的解决方案,这个解决方案虽然难看,但却解决了真正的问题。它有点像这样:

resp = requests.get(some_url, stream=True)
resp.raw._fp.fp._sock.settimeout(read_timeout)
# This will load the entire response even though stream is set
content = resp.content

您可以阅读完整的解释here