web-dev-qa-db-ja.com

Pythonハング/フリーズのリクエスト

どこからでも多くのWebページを取得するために、リクエストライブラリを使用しています。彼は適切なコードです:

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

しばらくすると、ページを取得している間、(同じWebページでは決して)ハング/フリーズします。中断したときのトレースバックは次のとおりです。

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

誰かがそれを引き起こしている可能性があることを知っていますか?または、(より重要なことですが)再試行できるように、一定の時間がかかる場合にそれを停止する方法を誰かが知っていますか?

11
Hobbit36

(読み取り) timeout を設定するように見えるかもしれません。

以下に沿ったもの:

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

(これにより、接続タイムアウトと読み取りタイムアウトの両方が5秒に設定されます。)

requestsでは、残念ながら、 docs で設定しても良いと言っていても、connectreadタイムアウトもデフォルトでは設定されていません。それ:

外部サーバーへのほとんどのリクエストは、サーバーがタイムリーに応答しない場合に備えて、タイムアウトをアタッチする必要があります。デフォルトでは、タイムアウト値が明示的に設定されていない限り、リクエストはタイムアウトしません。タイムアウトがないと、コードは数分以上ハングする可能性があります。

完全を期すため、接続タイムアウトは、クライアントがリモートマシンへの接続を確立するのをrequestsが待機する秒数です。 read timeoutは、サーバーから送信されたバイト間でクライアントが待機する秒数です。

13
randomir

文書化された「送信」関数にパッチを適用すると、すべての要求に対してこれが修正されます-多くの依存ライブラリとSDKでも同様です。ライブラリにパッチを適用する場合は、サポートされている/ドキュメント化された関数にパッチを適用してください。パッチを適用しないと、パッチの効果が失われて静かに失われる可能性があります。

import requests

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
0
Erik Aronesty

リクエストごとに指定する代わりに、タイムアウトをグローバルに設定するには:


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(...)
0