为什么request.get()不返回?request.get()使用的默认超时是多少?


92

在我的脚本中,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使用的默认超时是多少?


1
@ user2357112:有关系吗?我怀疑。
纳瓦兹

绝对重要。如果您提供要访问的URL和要使用的代理,我们可以看到在尝试发送类似请求时会发生什么。
user2357112支持Monica

1
@ user2357112:好的。编辑了问题。
纳瓦兹

2
您的代理人也不正确。您必须像这样指定它:proxies={'http': 'http://222.255.169.74:8080'}。这就是为什么没有超时就无法完成的原因。
伊恩·斯台普顿·科尔达斯科

Answers:


127

获取使用的默认超时是多少?

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

当您传递超时值时会发生什么?

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

3
我觉得你是对的。None表示无限(或“等待直到连接关闭”)。如果我自己通过超时,它将返回!
纳瓦兹

14
@User超时在HTTP上的效果与在HTTP上一样好
jaapz 2015年

通过谷歌搜索或其他方式似乎很难在文档中找到它。有人知道这在文档中显示在哪里吗?
wordforthewise


谢谢,print(requests.request.__doc__)尽管如此,在IPython中进行的工作更多。我想知道还有哪些其他可选参数request.get()
wordsforthewise

39

请求文档

您可以使用timeout参数在给定的秒数后告诉请求停止等待响应:

>>> 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)

注意:

超时不是整个响应下载的时间限制;相反,如果服务器在超时秒内未发出响应(更确切地说,在超时秒内未在基础套接字上接收到任何字节),则会引发异常。

对我而言,即使timeoutis为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))

注意:更改已合并到主请求项目中

3.使用evenletsignal已在类似问题中提到: python请求超时。获取整个响应


7
您永远不会回答默认值是什么
用户

Quote:您可以使用timeout参数在给定的秒数后告诉请求停止等待响应。几乎所有生产代码都应在几乎所有请求中使用此参数。否则可能会导致您的程序无限期挂起:注意,超时不是整个响应下载的时间限制;相反,如果服务器在超时秒内未发出响应(更确切地说,在超时秒内未在基础套接字上接收到任何字节),则会引发异常。如果未明确指定超时,则请求不会超时。
DDay

代码有错别字:从request.adapters导入TimeoutSauce
SinanÇetinkaya'19 / 1/19

4

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

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

信用: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的弃用警告,请检查参考底部附近的内容,以了解如何使用ClientTimeout数据结构。将每个链接引用的数据结构应用到上述原始代码的一种简单方法是:

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

2
@Nawaz Python 3.5+。谢谢您提出的问题,并使用Python版本更新了答案。这是合法的Python代码。请看一下aiohttp文档aiohttp.readthedocs.io/en/stable/index.html
Alex Polekha

这解决了其他方法无法解决的问题。Py 3.7。由于折旧,不得不使用... timeout = aiohttp.ClientTimeout(total = 60)与aiohttp.ClientSession(timeout = timeout)异步作为客户端:
Thom Ives

2

修补记录的“发送”功能将解决所有请求的问题,即使在许多依赖库和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本身也具有默认超时。


0

在我的情况下,“ requests.get永不返回”的原因是因为requests.get()尝试连接到首先用ipv6 ip解决的主机。如果在连接该ipv6 ip时出现问题并卡住,则只有在我明确设置并达到超时的情况下,它才会重试ipv4 iptimeout=<N seconds>

我的解决方案是用猴子修补 Python socket忽略ipv6(如果ipv4不起作用,则忽略 ipv4),这个答案这个答案都对我有用。

您可能想知道curl命令为何起作用,因为curl无需等待ipv6完成就可以连接ipv4。您可以使用strace -ff -e network -s 10000 -- curl -vLk '<your url>'命令跟踪套接字syscall 。对于python,strace -ff -e network -s 10000 -- python3 <your python script>可以使用command。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.