【问题标题】:Why doesn't requests.get() return? What is the default timeout that requests.get() uses?为什么 requests.get() 不返回? requests.get() 使用的默认超时是多少?
【发布时间】:2013-07-20 20:48:52
【问题描述】:

在我的脚本中,requests.get 永远不会返回:

import requests

print ("requesting..")

# This call never returns!
r = requests.get(
    "http://www.some-site.com",
    proxies = {'http': '222.255.169.74:8080'},
)

print(r.ok)

可能的原因是什么?有什么补救措施吗? get 使用的默认超时时间是多少?

【问题讨论】:

  • @user2357112:这有关系吗?我怀疑。
  • 这绝对很重要。如果您提供您尝试访问的 URL 和您尝试使用的代理,我们可以看到当我们尝试发送类似请求时会发生什么。
  • @user2357112:好的。编辑了问题。
  • 您的代理也不正确。您必须像这样指定它:proxies={'http': 'http://222.255.169.74:8080'}。这可能是它没有超时就无法完成的原因。

标签: python get python-requests


【解决方案1】:

get 使用的默认超时时间是多少?

默认超时为None,这意味着它将等待(挂起)直到连接关闭。

只需指定一个超时值,如下所示:

r = requests.get(
    'http://www.justdial.com',
    proxies={'http': '222.255.169.74:8080'},
    timeout=5
)

【讨论】:

  • 我认为你是对的。 None 表示无限(或“等到连接关闭”)。如果我自己通过超时,它会返回!
  • @User timeout 在 https 和 http 中的效果一样好
  • 这似乎很难通过谷歌搜索或其他方式在文档中找到。有人知道这出现在文档中的什么地方吗?
  • @Ehsan88 嗯?不。你在说什么?
【解决方案2】:

来自requests documentation

您可以告诉请求在给定的响应之后停止等待响应 带超时参数的秒数:

>>> requests.get('http://github.com', timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

注意:

超时不是整个响应下载的时间限制;相当, 如果服务器没有发出响应,则会引发异常 超时秒数(更准确地说,如果在 底层套接字超时秒数)。

即使timeout 是 1 秒,requests.get() 也需要很长时间才能返回。有几种方法可以解决这个问题:

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):
        if kwargs['connect'] is None:
            kwargs['connect'] = 5
        if kwargs['read'] is None:
            kwargs['read'] = 5
        super(MyTimeout, self).__init__(*args, **kwargs)

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

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

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

超时值将同时应用于连接和读取 超时。如果要设置值,请指定一个元组 分别:

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

注意:The change has since been merged to the main Requests project.

3.使用 evenletsignal 类似问题中已经提到的: Timeout for python requests.get entire response

【讨论】:

  • 你从来没有回答过默认值是什么
  • 引用:您可以使用超时参数告诉请求在给定秒数后停止等待响应。几乎所有生产代码都应在几乎所有请求中使用此参数。不这样做可能会导致您的程序无限期挂起:注意超时不是整个响应下载的时间限制;相反,如果服务器在 timeout 秒内没有发出响应(更准确地说,如果在 timeout 秒内底层套接字上没有收到任何字节),则会引发异常。如果没有明确指定超时,则请求不会超时。
  • 代码有错字:import requests from requests.adapters import TimeoutSauce
【解决方案3】:

我希望将默认超时轻松添加到一堆代码中(假设超时解决了您的问题)

这是我从提交到请求存储库的票证中获得的解决方案。

信用:https://github.com/kennethreitz/requests/issues/2011#issuecomment-477784399

解决方案是这里的最后几行,但我显示更多代码以获得更好的上下文。我喜欢使用会话进行重试行为。

import requests
import functools
from requests.adapters import HTTPAdapter,Retry


def requests_retry_session(
        retries=10,
        backoff_factor=2,
        status_forcelist=(500, 502, 503, 504),
        session=None,
        ) -> requests.Session:
    session = session or requests.Session()
    retry = Retry(
            total=retries,
            read=retries,
            connect=retries,
            backoff_factor=backoff_factor,
            status_forcelist=status_forcelist,
            )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    # set default timeout
    for method in ('get', 'options', 'head', 'post', 'put', 'patch', 'delete'):
        setattr(session, method, functools.partial(getattr(session, method), timeout=30))
    return session

那么你可以这样做:

requests_session = requests_retry_session()
r = requests_session.get(url=url,...

【讨论】:

    【解决方案4】:

    查看了所有答案,得出的结论是问题仍然存在。在某些站点上,请求可能会无限挂起,使用多处理似乎有点过头了。这是我的方法(Python 3.5+):

    import asyncio
    
    import aiohttp
    
    
    async def get_http(url):
        async with aiohttp.ClientSession(conn_timeout=1, read_timeout=3) as client:
            try:
                async with client.get(url) as response:
                    content = await response.text()
                    return content, response.status
            except Exception:
                pass
    
    
    loop = asyncio.get_event_loop()
    task = loop.create_task(get_http('http://example.com'))
    loop.run_until_complete(task)
    result = task.result()
    if result is not None:
        content, status = task.result()
        if status == 200:
            print(content)
    

    更新

    如果您收到关于使用 conn_timeout 和 read_timeout 的弃用警告,请查看THIS 参考底部附近,了解如何使用 ClientTimeout 数据结构。根据链接引用对上述原始代码应用此数据结构的一种简单方法是:

    async def get_http(url):
        timeout = aiohttp.ClientTimeout(total=60)
        async with aiohttp.ClientSession(timeout=timeout) as client:
            try:
                etc.
    

    【讨论】:

    • @Nawaz Python 3.5+。谢谢你的问题,用 Python 版本更新了答案。这是合法的 Python 代码。请查看 aiohttp 文档aiohttp.readthedocs.io/en/stable/index.html
    • 这解决了其他方法无法解决的问题。派 3.7。由于贬低,不得不使用 ... timeout = aiohttp.ClientTimeout(total=60) async with aiohttp.ClientSession(timeout=timeout) 作为客户端:
    【解决方案5】:

    在我的情况下,“requests.get 永远不会返回”的原因是因为requests.get() 尝试连接到首先使用 ipv6 ip 解析的主机。如果连接该 ipv6 ip 出现问题并卡住,那么只有当我明确设置 timeout=&lt;N seconds&gt; 并达到超时时,它才会重试 ipv4 ip

    我的解决方案是 monkey-patching 将 python socket 忽略 ipv6(或 ipv4,如果 ipv4 不起作用),this answerthis answer 都可以对我来说。

    您可能想知道为什么curl 命令有效,因为curl 无需等待 ipv6 完成即可连接 ipv4。您可以使用strace -ff -e network -s 10000 -- curl -vLk '&lt;your url&gt;' 命令跟踪套接字系统调用。对于python,可以使用strace -ff -e network -s 10000 -- python3 &lt;your python script&gt;命令。

    【讨论】:

      【解决方案6】:

      修补记录在案的“发送”功能将为所有请求修复此问题 - 即使在许多依赖库和 sdk 中也是如此。修补库时,请务必修补支持/记录的函数,而不是 TimeoutSauce - 否则您可能会默默地失去补丁的效果。

      import requests
      
      DEFAULT_TIMEOUT = 180
      
      old_send = requests.Session.send
      
      def new_send(*args, **kwargs):
           if kwargs.get("timeout", None) is None:
               kwargs["timeout"] = DEFAULT_TIMEOUT
           return old_send(*args, **kwargs)
      
      requests.Session.send = new_send
      

      没有任何超时的影响是相当严重的,使用默认超时几乎不会破坏任何东西——因为 TCP 本身也有默认超时。

      【讨论】:

        猜你喜欢
        • 2016-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多