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

时间:2013-07-22 07:31:58

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

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

6 个答案:

答案 0 :(得分:103)

  

使用的默认超时是什么?

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

传递超时值会发生什么?

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

答案 1 :(得分:29)

来自requests documentation

  

您可以告诉请求在给定后停止等待响应   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)
     

注意:

     

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

即使timeout为1秒,request.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
     

此代码应该使我们将读取超时设置为等于   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))

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

第3。使用类似问题中已提及的evenletsignal Timeout for python requests.get entire response

答案 2 :(得分:2)

回顾了所有答案并得出结论,问题仍然存在。在某些网站上,请求可能无限挂起,使用多处理似乎有点矫枉过正。这是我的方法(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)

答案 3 :(得分: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本身也具有默认超时。

答案 4 :(得分:1)

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

我的解决方案是 monkey-patching python socket忽略ipv6 (如果ipv4不起作用,则忽略ipv4),this answer或{ {3}}对我有用。

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

答案 5 :(得分:0)

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

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

信用: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,...