スキル

pythonでタイムアウト処理を追加してみる

DMMのWebサービスなどネットワークを経由して情報を取りに行くケースって非常に多いですよね。でも、全てのリクエストが確実に正常に終了して戻ってくるわけじゃなくって、長時間実行したり接続エラーになったりなどの問題も多く発生します。

いつまで経ってもリクエストの結果が戻ってこないで、忘れた頃に「TimeOut」ってマジか⁉︎って思っちゃいますけど、エラーになるだけまだマシで、1日経っても2日経っても一向に返ってこないこともあります。

そんな時に使うのは、強制的に終了させるタイムアウト機能です。意図的にタイムアウトを発生させることで、余計な手間を省くことができます。

DMMのWebサービス処理にタイムアウトを仕込む

例えば、pythonでDMMのWebサービスに接続しにいたんだけど、全然処理が戻ってこない。そんな時に60秒待ってダメならタイムアウトを発生させてエラー処理に進んだり、リトライをさせたりとかって処理を追加します。

DMMから商品情報を取得するプログラムはこちらで紹介しているので、この内容に修正を加える形で解説しますね。

タイムアウトを設定する処理を関数を確認

作ったpythonのコードはこれね。この中でDMMのWebサービスを使っているのは、リクエスト実行のところ。そうそう、赤字のところです。

#ライブラリインポート
import requests
import json
from pprint import pprint

#APIID
API_ID='uVurvdL*******F'

#アフィリエイトID
AFF_ID = 'XXXX-990'

#エンドポイントURL
url="https://api.dmm.com/affiliate/v3/ItemList?api_id="+API_ID+"&affiliate_id="+AFF_ID+"&site=DMM.com&hits=10&output=json"

#リクエスト実行
jsondata=requests.get(url).json()

#取得結果出力
pprint(jsondata)

ライブラリをインストール

タイムアウトを発生させる処理はインストールしないと使えないようです。色々なライブラリがあったんだけど、一番使いやすそうなのは、「timeout-decorator」というライブラリ

次のインストールコマンドでインストールできます。

!pip install timeout-decorator

タイムアウト用の関数を作成

タイムアウトさせる処理は、def関数にしてメイン処理から呼び出す形にする必要があるみたい。正しい方法は別にあるかもしれないんだけど、このやり方で成功したので、一旦まとめます。

変更部分は青字が追加、赤字が削除(コメント化)しています。

#ライブラリインポート
import requests
import json
from pprint import pprint
from timeout_decorator import timeout, TimeoutError

@timeout(10)
def run_request(pUrl):
   return requests.get(pUrl).json()

#APIID
API_ID='uVurvdL*******F'

#アフィリエイトID
AFF_ID = 'XXXX-990'

#エンドポイントURL
url="https://api.dmm.com/affiliate/v3/ItemList?api_id="+API_ID+"&affiliate_id="+AFF_ID+"&site=DMM.com&hits=10&output=json"

#リクエスト実行
#jsondata=requests.get(url).json()
jsondata=run_request(url)

#取得結果出力
pprint(jsondata)

関数「run_request」の作成

run_requestという関数を作成しています。そこで、requestを発行するようにコードを修正しています。もともと「リクエスト実行」していた箇所は、この「run_request」関数を呼び出すコードに変更しています。

関数にタイムアウト時間を設定

そして、run_request関数の上に@timeout(10)とタイムアウトコードをつけています。これで、直後の関数の処理許容時間が10秒にセットされ、10秒を超えて終了しない場合は、タイムアウトを発生させます。

実行結果を確認

実行結果はこれ。出力結果は問題ではないので、ちょっと端折りました。ただ、速攻で戻ってくるのでタイムアウトにはなりません。

{'request': {'parameters': {'affiliate_id': ' XXXX-990',
                            'api_id': 'uVurvdL*******F',
                            'hits': '2',
                            'output': 'json',
                            'site': 'DMM.com'}},
 'result': {'first_position': 1,
            'items': [{'URL': 'https://www.dmm.com/mono/hobby/-/detail/=/cid=cha_toumusical1746/',
                       'affiliateURL': 'https://al.dmm.com/?lurl=https%3A%2F%2Fwww.dmm.com%2Fmono%2Fhob
                      :        :        :        :        :        :        :        :        :

強制的にタイムアウトを作って確認

なので、処理直後にすぐに関数を終わらせないように処理を20秒ほど待機させてみます。

timeライブラリで処理を一時停止

timeライブラリのsleep関数を使って、処理を20秒間待機させます。変更したのは青字で記載しています。タイムアウト時刻より長い20秒間待機すればタイムアウトが発生するはずです。。

#ライブラリインポート
import requests
import json
import time
from pprint import pprint
from timeout_decorator import timeout, TimeoutError


@timeout(10)
def run_request(pUrl):
   time.sleep(20)
   return requests.get(pUrl).json()

#APIID
API_ID='uVurvdL*******F'

#アフィリエイトID
AFF_ID = 'XXXX-990'

#エンドポイントURL
url="https://api.dmm.com/affiliate/v3/ItemList?api_id="+API_ID+"&affiliate_id="+AFF_ID+"&site=DMM.com&hits=10&output=json"

#リクエスト実行
#jsondata=requests.get(url).json()
jsondata=run_request(url)

#取得結果出力
pprint(jsondata)

タイムアウト発生を確認

今度は、正しくタイムアウトが発生しました。出力結果はこんな感じです。

---------------------------------------------------------------------------
TimeoutError                              Traceback (most recent call last)
Input In [10], in <cell line: 23>()
     20 url="https://api.dmm.com/affiliate/v3/ItemList?api_id="+API_ID+"&affiliate_id="+AFF_ID+"&site=DMM.com&hits=2&output=json"
     22 #リクエスト実行
---> 23 jsondata=run_request(url)
     25 #取得結果出力
     26 pprint(jsondata)

ただ、これだと、タイムアウトすると処理が終わってしまいます。そうならないように元の処理にエラー発生時の処理を追加してあげる必要があります。

エラー発生時の処理を追加

タイムアウトが発生しても、プログラムが異常終了しないようにエラーハンドリングを追加する必要があります。

基本的には、エラーが発生したことを知らせるログを出力して処理を終了させるか、その処理をスキップして後続処理に進めることになります。

今回は、リクエストが失敗したら後続に進めないので、リトライを行なって、それでもダメなら終了する処理にしてみます。

エラーハンドリング1:エラーメッセージ&終了処理

想定外のエラーが発生した時にプログラムが異常終了するのは非常にカッコ悪いです。想定外エラーも想定内ですよ。ってアピールする意味でも例外処理は予測してコーディングをしましょう。

try 〜 exceptで例外発生時に備える

エラーハンドリングは try 〜 exceptを使います。呼び出した関数の先でエラーが発生した時は、exceptで指定した処理を行います。

下記では、run_request処理実行中に何らかのエラーが発生したら、エラーの内容をerr変数に格納して、exit処理の際にメッセージを出力しています。

#ライブラリインポート
import requests
import json
import time
import sys
from pprint import pprint
from timeout_decorator import timeout, TimeoutError


@timeout(10)
def run_request(pUrl):
   time.sleep(20)
   return requests.get(pUrl).json()

#APIID
API_ID='uVurvdL*******F'

#アフィリエイトID
AFF_ID = 'XXXX-990'

#エンドポイントURL
url="https://api.dmm.com/affiliate/v3/ItemList?api_id="+API_ID+"&affiliate_id="+AFF_ID+"&site=DMM.com&hits=10&output=json"

#リクエスト実行
#jsondata=requests.get(url).json()
try:
    jsondata=run_request(url)
except Exception as err:
    sys.exit(err)

#取得結果出力
pprint(jsondata)

処理結果

実行結果は次のようになります。先ほどのシステマチックなエラー情報はほとんど出ずにシンプルなメッセージを出力して処理が終了しています。

An exception has occurred, use %tb to see the full traceback.

SystemExit: 'Timed Out'

エラーハンドリング2:リトライ処理

再度、タイムアウトが発生しても、たまたまかもしれない。ネットワークがちょっと重かっただけかも、もう一回トライしたら成功するんじゃないか。って思うこともありますよね。

誰しも一度の失敗で終わらせるなんて厳しすぎます。もう一度、いやもう二度チャンスを与えてうまくいくか確認するという処理を加えてみます。

繰り返し分を入れてリトライを実装

for文を使って実装しています。run_requestを

#リクエスト実行
for i in range(3):
    try:
        jsondata=run_request(url)
    except Exception as err:
        sys.exit(err)
        print(err)
        print("リトライ:",i+1,"回目")
        continue
    break

if len(jsondata)==0:
    sys.exit("エラーで終了")

処理結果

'Timed Out'
リトライ: 1 回目
'Timed Out'
リトライ: 2 回目
'Timed Out'
リトライ: 3 回目
An exception has occurred, use %tb to see the full traceback.

SystemExit: エラーで終了

pythonの学習方法

python自体は構造が非常にシンプルなので、独学でも学ぶことは可能です。最低限把握しておくべき基礎についてはこちらの記事でまとめています。

でも、すぐに副業で収益を得たいという思いがあるならスクール利用がお得です。

初心者にもわかりやすく解説している書籍とスクールをそれぞれご紹介しますので、pythonの習得のお役に立ててください。

pythonの基礎知識について

今回、pythonの基礎知識については、触れていませんが、そのやり方などpythonの基本的な仕様を確認したい方は、別途記事を書くのでそれまでお待ちください。なお、こちらの書籍は比較的pythonの基礎学習には向いている本だと思います。

また、python自体は難しいプログラミング言語ではありませんが、独学でゼロから学ぶのはそれなりに時間がかかります。簡単なプログラミングだからこそ、短期間のスクールに通うことで、見違えたようにプログラミングのスキルが上達します。

独学ではなく、スクールのススメ

副業やフリーランスを目指すのであれば、スクールに通うことを強くお勧めします。フリーランスとして稼ぐノウハウなどもセットで教えてくれるスクールが最近では非常に増えていますので、今後のキャリアを考える意味でも非常に有益です。

いずれ、フリーになれれば・・・とか、いずれ副業で稼げればいいな。って考えていたら、いつ実現するのかわからなくなります。

サクッとプログラミングを習得して夢のフリーランス生活を勝ち取りましょう。

テックアカデミー

TechAcademyは、1人ではプログラミング学習が続かない方のための、パーソナルメンターがつく「オンラインブートキャンプ」です。

オンラインブートキャンプの特徴は下記の3つがあります。

  1. スクールに通わなくても、自宅などでオンライン学習できる
  2. わからないことはいつでもチャットでメンターに質問できる
  3. パーソナルメンターがついてオリジナルサービスやオリジナルアプリの公開までサポート

自分のオリジナルサービスやアプリを開発しながらプログラミングを実践的に学んでいただき、 わからないことはいつでも現役エンジニアのメンターに相談することができます。

現在は、6つのコースがありいずれも最短4週間で完結します。

■提供中のコース

  • Webアプリコースコース
  • iPhoneアプリコース
  • Androidアプリコース
  • WordPressコース
  • Webデザインコース
  • アプリUI/UXデザインコース

詳細はこちらをご覧ください。

-スキル
-, , , ,