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)
Hie*_*ieu 33
来自请求文档:
您可以使用timeout参数告知请求在给定秒数后停止等待响应:
Run Code Online (Sandbox Code Playgroud)>>> 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
Run Code Online (Sandbox Code Playgroud)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
如果为超时指定单个值,则如下所示:
Run Code Online (Sandbox Code Playgroud)r = requests.get('https://github.com', timeout=5)超时值将应用于连接和读取超时.如果要单独设置值,请指定元组:
Run Code Online (Sandbox Code Playgroud)r = requests.get('https://github.com', timeout=(3.05, 27))
注意:此后的更改已合并到主要请求项目中.
3.使用evenlet或signal如类似问题中已提到的:
python requests.get整个响应的超时
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 本身也有默认超时。
就我而言,“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>可以使用命令。
查看所有答案并得出结论,问题仍然存在。在某些站点上,请求可能会无限挂起,并且使用多重处理似乎有点过分了。这是我的方法(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)
我想要一个默认超时很容易添加到一堆代码中(假设超时解决了你的问题)
这是我从提交给请求存储库的票证中获取的解决方案。
信用: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)
| 归档时间: |
|
| 查看次数: |
69912 次 |
| 最近记录: |