Python请求挂起/冻结

时间:2017-07-23 15:48:57

标签: python python-3.x python-requests

我正在使用请求库从某个地方获取大量网页。他是相关的代码:

response = requests.Session()
retries = Retry(total=5, backoff_factor=.1)
response.mount('http://', HTTPAdapter(max_retries=retries))
response = response.get(url)

过了一会儿,它只是在获取页面时挂起/冻结(从不在同一个网页上)。这是我打断它时的追溯:

File "/Users/Student/Hockey/Scrape/html_pbp.py", line 21, in get_pbp
  response = r.read().decode('utf-8')
File "/anaconda/lib/python3.6/http/client.py", line 456, in read
  return self._readall_chunked()
File "/anaconda/lib/python3.6/http/client.py", line 566, in _readall_chunked
  value.append(self._safe_read(chunk_left))
File "/anaconda/lib/python3.6/http/client.py", line 612, in _safe_read
  chunk = self.fp.read(min(amt, MAXAMOUNT))
File "/anaconda/lib/python3.6/socket.py", line 586, in readinto
  return self._sock.recv_into(b)
KeyboardInterrupt

有人知道是什么原因引起的吗?或者(更重要的是)是否有人知道如果需要超过一定的时间才能阻止它,以便我可以再试一次?

3 个答案:

答案 0 :(得分:10)

似乎设置(阅读)timeout可能对您有帮助。

有些事情:

response = response.get(url, timeout=5)

(这会将连接和读取超时都设置为5秒。)

遗憾的是,在requests中,默认设置 connect read 超时,即使docs说出来了很高兴设置它:

  

如果服务器未及时响应,则对外部服务器的大多数请求应附加超时。默认情况下,除非明确设置超时值,否则请求不会超时。如果没有超时,您的代码可能会挂起几分钟或更长时间。

为了完整起见,连接超时是等待客户端与远程计算机建立连接的秒数requests以及读取超时 strong>是客户端在从服务器发送的字节之间等待的秒数。

答案 1 :(得分:1)

修补记录的“发送”功能将针对所有请求解决此问题-即使在许多依赖的库和sdk中也是如此。修补库时,请务必修补受支持/已记录的功能,否则您可能会无声无息地失去修补效果。

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本身也有超时。

在Windows上,默认的TCP超时为240秒,TCP RFC建议RTO * retry至少为100秒。在该范围内的某个位置是安全的默认值。

答案 2 :(得分:0)

要全局设置超时,而不是在每个请求中都指定:


from requests.adapters import TimeoutSauce

REQUESTS_TIMEOUT_SECONDS = float(os.getenv("REQUESTS_TIMEOUT_SECONDS", 5))

class CustomTimeout(TimeoutSauce):
    def __init__(self, *args, **kwargs):
        if kwargs["connect"] is None:
            kwargs["connect"] = REQUESTS_TIMEOUT_SECONDS
        if kwargs["read"] is None:
            kwargs["read"] = REQUESTS_TIMEOUT_SECONDS
        super().__init__(*args, **kwargs)


# Set it globally, instead of specifying ``timeout=..`` kwarg on each call.
requests.adapters.TimeoutSauce = CustomTimeout


sess = requests.Session()
sess.get(...)
sess.post(...)