pythonでHTTP通信するときに大体使うrequestsで、timeoutエラーの種類で問題の切り分けがしたくて調べたのでちょっとまとめ。

TL;DR

公式ドキュメントのExceptionsを見るとわかりやすい。
中でもタイムアウト関連のエラーは以下の二つ:

connect timeout

requests.exceptions.ConnectTimeout例外が投げられる。
相手のサーバと接続を確立する時に時間がかかりすぎたtimeout。
公式ドキュメント

read timeout

requests.exceptions.ReadTimeout例外が投げられる。
相手のサーバからレスポンスが返ってくるまでに時間がかかりすぎたtimeout。
公式ドキュメント

両方まとめてcatchしたい時は

requests.exceptions.Timeout例外が設定されていて、ConnectTimeout, ReadTimeoutどちらも含まれます。


もうちょっと詳しく

そもそもRequestsの例外の中には、RequestException(曖昧な例外)やConnectionErrorが設定されていて、明らかに設定がおかしい場合や早期に接続エラーが起きた場合には、レスポンスを待つ前にこの例外が投げられます。
それらが起きずにtimeoutエラーが起きた場合は、接続の確立で相手からレスポンスが返ってこない(ConnectTimeout)か、接続確率はできてるけど接続先がデータの準備などで時間がかかりすぎて設定したtimeout時間内にレスポンスが返ってこない(Readtimeout)かの二択。
なので、ConnectTimeoutが起きた時には安全に再試行できると公式ドキュメントにも書かれてます(少なくともリクエスト時の設定に問題がある訳じゃないから)。

Timeout例外が出た時にする事は下記の感じになると思います:

  • ConnectTimeoutが出た場合は、リクエストを投げてる相手のサーバがダウンしてないか、ネットワーク設定に問題がないかを確認する。
  • ReadTimeoutが出た場合は、接続の確立には成功していて、向こうのレスポンスに時間がかかっているので、リクエストするサーバ側にも責任がある場合はチューニングする。そうでない場合は一度timeout時間を伸ばして成功するか様子を見る。

デフォルトのtimeout時間は設定されていないので注意する

明示的に設定しない限り、timeoutのデフォルトはNoneです。つまりtimeoutしません。
基本的にはtimeoutを設定して、上記のtimeoutエラーで原因がわかるようにした方が良さそうです。

import requests
try:
    # connect timeout, read timeoutどちらも同じ値で良い場合
    res = requests.get('有効なURL', timeout=30)

    # connect timeout=30, read timeout=60とかで分けたい場合
    res = requests.get('有効なURL', timeout=(30, 60))
except requests.exceptions.ConnectTimeout:
    print("接続確立のエラー")
except requests.exceptions.ReadTimeout:
    print("レスポンスが遅すぎたエラー")