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

Naw*_*waz 79 python get python-requests

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

import requests

print ("requesting..")

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

print(r.ok)
Run Code Online (Sandbox Code Playgroud)

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

ron*_*man 113

什么是使用的默认超时?

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

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

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

  • @User超时与https一样好,与http一样好 (14认同)
  • 我觉得你是对的.`None`表示无限(或"等到连接关闭").如果我自己通过超时,它会返回! (2认同)
  • @wordsforthewise http://docs.python-requests.org/en/master/user/quickstart/#timeouts (2认同)

Hie*_*ieu 33

来自请求文档:

您可以使用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)
Run Code Online (Sandbox Code Playgroud)

注意:

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

我发生了很多事情,即使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
Run Code Online (Sandbox Code Playgroud)

此代码应该使我们将读取超时设置为等于连接超时,这是您在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)
Run Code Online (Sandbox Code Playgroud)

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

r = requests.get('https://github.com', timeout=(3.05, 27))
Run Code Online (Sandbox Code Playgroud)

注意:此后的更改已合并到主要请求项目中.

3.使用evenletsignal如类似问题中已提到的: python requests.get整个响应的超时

  • 你永远不会回答默认是什么 (4认同)

Eri*_*sty 10

修补记录的“发送”函数将为所有请求修复此问题 - 即使在许多依赖库和 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
Run Code Online (Sandbox Code Playgroud)

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


林果皞*_*林果皞 7

就我而言,“requests.get 永远不会返回”的原因是因为requests.get()尝试连接到首先使用 ipv6 ip 解析的主机。如果连接该 ipv6 ip 时出现问题并卡住,则仅当我显式设置并达到超时时,它才会重试ipv4 ip 。timeout=<N seconds>

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

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


Ale*_*kha 5

查看所有答案并得出结论,问题仍然存在。在某些站点上,请求可能会无限挂起,并且使用多重处理似乎有点过分了。这是我的方法(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)
Run Code Online (Sandbox Code Playgroud)

更新

如果您收到有关使用 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.
Run Code Online (Sandbox Code Playgroud)

  • @Nawaz Python 3.5+。谢谢你的问题,用Python版本更新了答案。这是合法的Python代码。请查看 aiohttp 文档 http://aiohttp.readthedocs.io/en/stable/index.html (2认同)

Tim*_*son 5

我想要一个默认超时很容易添加到一堆代码中(假设超时解决了你的问题)

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

信用: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
Run Code Online (Sandbox Code Playgroud)

那么你可以做这样的事情:

requests_session = requests_retry_session()
r = requests_session.get(url=url,...
Run Code Online (Sandbox Code Playgroud)