質問: pythonリクエストのタイムアウト。レスポンス全体を取得する。 私はウェブサイトのリストに関する統計を集めています、そして、私は単純化するためにそれのために要求を使っています。これが私のコードです:data=[] websites=['http://google.com', 'http://bbc.co.uk'] for w in websites: r= requests.get(w, verify=false) data.append( (r.url, len(r.content), r.elapsed.total_seconds(), str([(l.status_code, l.url) for l in r.history]), str(r.headers.items()), str(r.cookies.items())) ) さて、私はrequests.getが10秒後にタイムアウトするようにしてループが動かなくならないようにします。この質問は興味をそそられています 以前の でも答えはどれもきれいではありません。私はいい答えを得るためにこれにいくらかの恩恵をかけるつもりです。おそらくリクエストを使用しないのが良い考えであると私は聞いていますが、それではどのように私はリクエストが提供する素晴らしいものを手に入れるべきですか。 (タプルの中のもの)
回答: タイムアウトを作成するには、 シグナル を使用できます。この問題を解決する最善の方法は、おそらく アラーム信号のハンドラとして例外を設定します 10秒遅れてアラーム信号を呼び出す try-except-finallyブロック内で関数を呼び出します。 関数がタイムアウトした場合、exceptブロックに到達します。 finallyブロックではアラームを中止しますので、後で警告されることはありません。 これがいくつかのコード例です:import signal from time import sleep class timeoutexception(exception): """ simple exception to be called on timeouts. """ pass def _timeout(signum, frame): """ raise an timeoutexception. this is intended for use as a signal handler. the signum and frame arguments passed to this are ignored. """ # raise timeoutexception with system default timeout message raise timeoutexception() # set the handler for the sigalrm signal: signal.signal(signal.sigalrm, _timeout) # send the sigalrm signal in 10 seconds: signal.alarm(10) try: # do our code: print('this will take 11 seconds...') sleep(11) print('done!') except timeoutexception: print('it timed out!') finally: # abort the sending of the sigalrm signal: signal.alarm(0) これにはいくつか注意点があります。 スレッドセーフではない、シグナルは常にメインスレッドに配信されるので、これを他のスレッドに入れることはできません。 シグナルのスケジューリングと実際のコードの実行の後、わずかな遅れがあります。これは、例が10秒間しかスリープしていなくてもタイムアウトすることを意味します。 しかし、それはすべて標準のpythonライブラリにあります! sleep関数のインポートを除き、インポートは1つだけです。あなたがタイムアウトを多くの場所で使用しようとしているならば、あなたは簡単にtimeoutexception、_timeoutとシングリングを関数に入れて、それを単に呼び出すことができます。あるいは、デコレータを作成してそれを関数に配置することもできます。下記の回答を参照してください。これを "context manager" として設定することもできますので、withステートメントで使用できます。import signal class timeout(): """ timeout for use with the `with` statement. """ class timeoutexception(exception): """ simple exception to be called on timeouts. """ pass def _timeout(signum, frame): """ raise an timeoutexception. this is intended for use as a signal handler. the signum and frame arguments passed to this are ignored. """ raise timeout.timeoutexception() def __init__(self, timeout=10): self.timeout = timeout signal.signal(signal.sigalrm, timeout._timeout) def __enter__(self): signal.alarm(self.timeout) def __exit__(self, exc_type, exc_value, traceback): signal.alarm(0) return exc_type is timeout.timeoutexception # demonstration: from time import sleep print('this is going to take maximum 10 seconds...') with timeout(10): sleep(15) print('no timeout?') print('done') このコンテキストマネージャのアプローチで考えられる欠点の1つは、コードが実際にタイムアウトしたかどうかがわからないということです。出典と推奨読書: シグナルに関するドキュメンテーション @david narayanによるタイムアウトに関するこの回答 。彼は上記のコードを デコレータ としてまとめました。