複数のURLがちゃんと生きているか、応答がどれくらい速いかをまとめて確かめたい場面はよくあります。今回はPythonのClickとrequestsを組み合わせて、複数URLの状態と応答時間を1行ずつ表示するCLIツールを実際に作って動かしました。
checkコマンドで実URLを計測し、demoコマンドではローカルにサーバを立てて外部通信なしで挙動を確認できます。初心者でも追体験できるように、コードを小さく分けて解説します。
Clickの基本概念、要件定義、実装、動作確認までを順番に学べる構成です。動画は目次から確認したい場面へ移動でき、本文だけでも手順と考え方が完結します。
動画の内容をテキストで確認する
オープニング。Clickとrequestsを使ってURL監視CLIを作るカリキュラムを始めます。概要紹介。
Clickとrequestsの役割と使い方を学ぶURL監視CLIの要件を整理する 完成コードと実行結果を確認する 最後に実コマンドとファイル状態で完成挙動を確かめる 具体的にやること。
checkコマンドで1件以上のURL引数を受け取り応答を計測するdemoコマンドでローカルサーバを起動し正常遅延エラーの3種類を計測するformat_rowで判定マークとステータスと応答時間を1行に整形するreportで結果一覧と正常件数のサマリを表示する --timeoutオプションで1件あたりの待機秒数を指定できる 実装環境・必須アプリ。
OS:Windows 11 Pro Python:3.13.3シェル:PowerShell 5.1必須アプリ:コードエディター、ターミナル、エクスプローラー パッケージ:pip、click、requests Clickとrequestsとは。
Click:Clickは、Pythonでコマンドラインツールを作るためのライブラリですrequests:requestsは、PythonからHTTP通信を行うための定番ライブラリです Clickとrequestsで作るURL監視CLIの要点。
format_rowはstatusがNoneのときERR(例外名)という文字列を表示することreportの最後にok_countとlen(results)を使った正常件数のサマリ行を表示することdemoコマンドは--roundsオプションの回数だけreportを繰り返し実行できること URL監視CLIの要件定義。
check https://example.comのようにURLを1件以上渡すと応答一覧と件数サマリが表示される 存在しないURLや接続できないURLではNG判定とERR(例外名)が表示されるdemoを実行するとデモ用サーバ起動ログと3件の計測結果と停止ログが表示されるdemo --rounds 2ではラウンドごとの見出し付きで計測が2回表示される 確認1/4: checkコマンドでローカルURLの状態を計測する。
ローカルに簡易サーバを立てたうえでcheckコマンドを呼び出し、応答が一覧表示されることを確認します。実行2/4: checkコマンドでエラー応答を計測する。/errorエンドポイントを計測し、NG判定と500ステータスが表示されることを確認します。
確認2/4: checkコマンドでエラー応答を計測する。/errorエンドポイントを計測し、NG判定と500ステータスが表示されることを確認します。実行3/4: demoコマンドでローカルサーバの一括計測を確認する。
demoコマンドを実行し、正常・遅延・エラーの3件がまとめて計測されることを確認します。確認3/4: demoコマンドでローカルサーバの一括計測を確認する。demoコマンドを実行し、正常・遅延・エラーの3件がまとめて計測されることを確認します。
実行4/4: demoコマンドで--roundsオプションを使い複数回計測する。--rounds 2を指定してdemoを実行し、ラウンドごとに計測結果が表示されることを確認します。確認4/4: demoコマンドで--roundsオプションを使い複数回計測する。
--rounds 2を指定してdemoを実行し、ラウンドごとに計測結果が表示されることを確認します。学習内容のまとめ。
@click.group()でcli関数を作り複数のサブコマンドをまとめることresponse.okはステータスコードが400未満のときTrueになり200番台以外でも真になり得ることformat_rowはstatusがNoneのときERR(例外名)という文字列を表示することrequestsのtimeoutは接続と読み取り待機にかける秒数を1回のリクエストごとに制限する値である 小さく実行確認しながら完成状態まで段階的に組み立てる エンディング。
Python研修はCodeCampでご確認ください。
目次
- Clickとrequestsとは
- Python・Clickで開発する場合の環境構築
- PythonのClickとrequestsで作るURL監視CLIの要件定義
- ClickとrequestsでURL監視CLIを作る際の重要ポイント
- PythonのClickとrequestsで作るURL監視CLIの完成コード
- PythonのClickとrequestsで作ったURL監視CLIの動作確認
- PythonのClickとrequests利用時に起きやすいエラーと対処法
- PythonのClickの引数とrequestsのタイムアウト注意点
- URL監視CLIをPython運用で活かす場面
- PythonのClickとrequestsで作ったCLIの総括
- 参考にした一次情報
Clickとrequestsとは
今回使用する主要なライブラリについて、役割と使い分けを順番に確認します。
ClickでCLIコマンドを組み立てる仕組み
Clickは、Pythonでコマンドラインツールを作るためのライブラリです。@click.group()でコマンド全体をまとめ、@cli.command()で個々のサブコマンドを登録できます。
今回のCLIではcheckとdemoという2つのコマンドをClickの仕組みで公開しており、click.argumentやclick.optionを使って利用者が指定できる値を定義しています。関数にデコレータを重ねるだけでコマンドラインの入力解析が組み上がる点は、Clickを使う大きな利点といえるでしょう。
Clickでコマンドを組み立てる際に押さえておきたい設定について確認します。
- @click.group()でcli関数を作り複数のサブコマンドをまとめること
- @cli.command()でcheckとdemoをサブコマンドとして登録すること
- click.argumentのnargs=-1とrequired=Trueによりurlsは1件以上の指定が必須なこと
- click.optionのdefaultとshow_default=Trueにより--helpへ既定値が表示されること
requestsでHTTP応答を取得し判定する仕組み
requestsは、PythonからHTTP通信を行うための定番ライブラリです。requests.get(url, timeout=timeout)でURLへアクセスし、返ってきたResponseオブジェクトのokやstatus_codeを見ることで応答の状態を判断できます。timeoutは1回のリクエストにおける接続や応答待ちの秒数を制限するための値です。
通信が失敗した場合はrequests.RequestExceptionを捕まえて、エラー内容をerrorに記録します。
requestsで通信結果を判断する際に知っておきたい挙動について確認します。
- response.okはステータスコードが400未満のときTrueになり200番台以外でも真になり得ること
- timeoutは接続や読み取り待ちにかける秒数を1回のリクエストごとに制限する値であること
- requests.RequestExceptionを捕捉したときはstatusをNoneにしerrorへ例外クラス名を格納すること
Python・Clickで開発する場合の環境構築
この記事のセットアップ手順と掲載コードは、Windows 11 Pro、PowerShell 5.1、Python 3.13.3で動作確認しています。仮想環境を有効化せず、その中のPythonを直接指定するため、以下のコマンドはPowerShellとコマンドプロンプト(cmd)の両方で使えます。
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install --upgrade pip
.\.venv\Scripts\python.exe -m pip install click requests
macOS・Linuxでは仮想環境内のPythonパスが異なります。今回の動作確認環境とは異なるため、以下は環境差分を補う参考手順です。
python3 -m venv .venv
./.venv/bin/python -m pip install --upgrade pip
./.venv/bin/python -m pip install click requests
- demoコマンドはローカルにHTTPサーバを立てるため、ファイアウォールで127.0.0.1宛のローカル通信が許可されている必要があります。
- checkで実サイトを叩く場合はhttp://やhttps://を含むフルURLを渡してください(スキーム無しはエラーになります)。
PythonのClickとrequestsで作るURL監視CLIの要件定義
目的は、ClickとrequestsでURLを引数に取り応答状況と応答時間を計測して1行ずつ表示できるCLI監視ツールを構築することです。
対象者として、PythonでCLIコマンドの作り方やrequestsを使ったHTTP応答確認の基礎を学びたい人を想定しています。
完成物は、Clickのcheck・demoコマンドとrequestsによる応答計測を備えたURL監視CLIの完成コードと動作確認です。
実装へ入る前に、機能・品質・受け入れ条件を分けて確認します。
機能要件
- checkコマンドで1件以上のURL引数を受け取り応答を計測する
- demoコマンドでローカルサーバを起動し正常遅延エラーの3種類を計測する
- format_rowで判定マークとステータスと応答時間を1行に整形する
- reportで結果一覧と正常件数のサマリを表示する
- --timeoutオプションで1件あたりの待機秒数を指定できる
- --roundsオプションでdemoの計測を繰り返せる
非機能要件
- requestsのtimeoutは接続と読み取り待機にかける秒数を1回のリクエストごとに制限する値である
- requests.RequestExceptionを捕捉してCLIが例外で落ちないようにする
- demoコマンドはfinallyでサーバをshutdownしてリソースを解放する
- サーバのポートは0指定によりOSが空きポートを自動割り当てる
- demoコマンドは外部通信を行わないがlocalhostへのHTTP通信は実際に行う
実装方針
今回はClickとrequestsの基本動作を追いやすくするため、URL監視CLI本体を1つのPythonファイルへまとめます。
入力、判定、結果表示の役割を分け、実行結果を確認しながら機能を積み上げます。
URL監視CLIを安全に組み立てるための実装方針は次のとおりです。
- requestsのtimeoutは接続と読み取り待機にかける秒数を1回のリクエストごとに制限する値である
- requests.RequestExceptionを捕捉してCLIが例外で落ちないようにする
- demoコマンドはfinallyでサーバをshutdownしてリソースを解放する
- サーバのポートは0指定によりOSが空きポートを自動割り当てる
- demoコマンドは外部通信を行わないがlocalhostへのHTTP通信は実際に行う
完成と判断する条件
- check https://example.comのようにURLを1件以上渡すと応答一覧と件数サマリが表示される
- 存在しないURLや接続できないURLではNG判定とERR(例外名)が表示される
- demoを実行するとデモ用サーバ起動ログと3件の計測結果と停止ログが表示される
- demo --rounds 2ではラウンドごとの見出し付きで計測が2回表示される
ClickとrequestsでURL監視CLIを作る際の重要ポイント
URL監視CLIの中核となるのは、計測した結果を人が読みやすい形に整えて表示するロジックです。check_allが複数のURLをまとめて計測し、format_rowが1件ずつを整形した文字列に変換し、reportがそれらを出力しながら正常件数のサマリも添えます。
demoコマンドではこの一連の流れを、ローカルに起動したサーバへ実際にHTTP通信を行うことで、外部ネットワークに接続せずに確認できるようになっています。
計測結果の整形と表示で押さえておきたい細部について確認します。
- format_rowはstatusがNoneのときERR(例外名)という文字列を表示すること
- reportの最後にok_countとlen(results)を使った正常件数のサマリ行を表示すること
- demoコマンドは--roundsオプションの回数だけreportを繰り返し実行できること
- demoコマンドはfinallyでサーバをshutdownしてから終了メッセージを表示すること
check_allで複数URLを順番に計測する処理
check_allはurlsに含まれる各URLに対してcheck_urlを呼び出し、その結果をリストとしてまとめます。リスト内包表記を使っているため、呼び出した順番のまま結果が並ぶ仕組みです。
check_allが返すリストの要素について確認します。
- 1件ごとのcheck_urlの結果辞書
- 呼び出した順番のまま並ぶ計測結果
format_rowが1行の表示を組み立てるルール
format_rowはstatusがNoneかどうかでラベルを切り替え、okの真偽でOKかNGのマークを選びます。f文字列の書式指定で桁数をそろえているため、複数行を並べても読みやすくなっています。
format_rowが1行にまとめる要素について確認します。
- OKまたはNGの判定マーク
- 6桁で右寄せしたステータスラベル
- 小数第1位までの応答時間ミリ秒
- 計測対象のURL
demoコマンドがローカルサーバで一連の流れを見せる仕組み
demoコマンドはThreadingHTTPServerをポート0で起動し、OSに割り当てられた空きポートを使用します。daemonスレッドでサーバを動かしながら、/ok, /slow, /errorの3つのURLをcheck_allで計測し、finallyでサーバをshutdownしています。
デモ用サーバが用意する3つのエンドポイントの応答について確認します。
- /okは200 okをすぐ返す
- /slowはslow_msミリ秒待ってから200 slow-okを返す
- /errorは500 server-errorを返す
PythonのClickとrequestsで作るURL監視CLIの完成コード
動画の内容をテキストで確認する
INTRO: Clickを使ったCLI開発を開始。コードを1行ずつ入力し、節目ごとに実行結果を確かめながら完成させます。LINE 001: モジュール全体の説明。
このファイルがClickとrequestsを使ってURLを監視するCLIツールであることを説明するドキュメント文字列です。複数のURLの状態と応答時間を確認する目的で作られています。LINE 003: インポート部の目的コメント。
これから読み込むライブラリの節であることを示すコメントです。ここから必要な外部ライブラリと標準ライブラリを準備していきます。LINE 004: スレッド操作モジュールの読み込み。
threadingモジュールを読み込んでいます。デモ用サーバをバックグラウンドで動かすために後の処理で使います。LINE 005: 時間計測モジュールの読み込み。
timeモジュールを読み込んでいます。応答時間の計測や待機処理に利用します。LINE 006: 簡易HTTPサーバ機能の読み込み。
http.serverからBaseHTTPRequestHandlerとThreadingHTTPServerを読み込んでいます。デモ用のローカルサーバを作るために使います。LINE 008: CLI作成ライブラリの読み込み。
clickライブラリを読み込んでいます。コマンドライン引数の受け取りや画面出力に使います。LINE 009: HTTP通信ライブラリの読み込み。
requestsライブラリを読み込んでいます。実際にURLへアクセスして応答を取得するために使います。LINE 012: 単一URL計測関数の目的コメント。
1件のURLを計測する関数がこれから定義されることを示すコメントです。ここでURLへのアクセス結果をまとめる処理を実装します。LINE 013: check_url関数の定義。
URLとタイムアウト秒数を受け取るcheck_url関数を定義しています。デフォルトのタイムアウトは5秒に設定されています。LINE 014: 関数の役割を説明するドキュメント文字列。
1つのURLへGETリクエストを送り、状態と応答時間をまとめた辞書を返す関数であることを説明しています。LINE 015: 計測開始時刻の記録。perf_counterを使って処理開始時点の時刻を記録しています。
この後の応答時間計算の基準になります。LINE 016: 例外処理の開始。通信中に発生するかもしれないエラーを捕まえるため、try文でこれ以降の処理を囲んでいます。
LINE 017: GETリクエストの送信。requestsライブラリを使って指定したURLへGETリクエストを送信し、その結果をresponseに受け取っています。LINE 018: 応答時間の計算。
現在時刻から開始時刻を引いてミリ秒単位の応答時間を計算しています。この値が結果に含まれます。LINE 019: 正常時の結果辞書の開始。
通信が成功した場合に返す辞書の作成を開始しています。ここからURLや状態などの情報を1つにまとめていきます。LINE 020: 対象URLの格納。
計測したURLそのものを辞書のurlキーに格納しています。後で結果の一覧表示に使われます。LINE 021: 成功判定の格納。
レスポンスのokプロパティを使い、通信が成功したかどうかをokキーに格納しています。LINE 022: HTTPステータスコードの格納。サーバから返されたステータスコードをstatusキーに格納しています。
200や404といった値が入ります。LINE 023: 応答時間の格納。先ほど計算した応答時間を小数点1桁に丸めてelapsed_msキーに格納しています。
LINE 024: エラー情報なしの明示。通信が成功した場合はエラーが発生していないため、errorキーにNoneを設定しています。LINE 025: 結果辞書の閉じ括弧。
正常時の結果辞書の定義を終えている行です。この辞書がcheck_url関数の戻り値になります。LINE 026: 通信例外の捕捉。
requests.RequestExceptionが発生した場合の処理を開始しています。タイムアウトや接続失敗などをここでまとめて扱います。LINE 027: 失敗時の応答時間の計算。
例外が発生した場合でも開始時刻からの経過時間を計算し、失敗までにかかった時間を求めています。LINE 028: 失敗時の結果辞書の開始。通信が失敗した場合に返す辞書の作成を開始しています。
正常時と同じ形式で情報をまとめます。LINE 029: 対象URLの格納(失敗時)。通信に失敗したURLをurlキーに格納しています。
どのURLで失敗したか分かるようにするためです。LINE 030: 失敗判定の格納。通信が失敗したことを示すため、okキーにFalseを設定しています。
LINE 031: ステータスコードなしの明示。通信が失敗しているためステータスコードが取得できず、statusキーにNoneを設定しています。LINE 032: 失敗時の応答時間の格納。
失敗するまでにかかった時間を小数点1桁に丸めてelapsed_msキーに格納しています。LINE 033: 例外の種類の格納。発生した例外のクラス名を文字列としてerrorキーに格納し、どんな種類のエラーか分かるようにしています。
LINE 034: 失敗時の結果辞書の閉じ括弧。失敗時の結果辞書の定義を終えている行です。この辞書がエラー発生時の戻り値になります。
RUN 1/7: check_url関数が定義できた時点を確認する。check_url関数までの定義が揃った時点です。接続できないポートへアクセスして例外処理の挙動を確認します。
CHECK 1/7: 途中実行に成功。{'url': 'http://127.0.0.1:1', 'ok': False, 'status': None, 'elapsed_ms': 1.7, 'error': 'ConnectionError'} LINE 037: 一括計測関数の目的コメント。
複数のURLをまとめて計測する関数がこれから定義されることを示すコメントです。LINE 038: check_all関数の定義。複数のURLとタイムアウト秒数を受け取るcheck_all関数を定義しています。
LINE 039: 関数の役割を説明するドキュメント文字列。複数URLを順番に計測して結果のリストを返す関数であることを説明しています。LINE 040: 全URLの計測結果をリスト化。
リスト内包表記を使い、渡された全てのURLに対してcheck_url関数を呼び出し、その結果をリストとしてまとめて返しています。RUN 2/7: check_allで複数URLをまとめて計測できる時点を確認する。check_allが定義できた時点です。
複数のURLを渡して結果件数を確認します。CHECK 2/7: 途中実行に成功。2 LINE 043: 結果整形関数の目的コメント。
計測結果を人間が読みやすい形に整形する処理がこれから定義されることを示すコメントです。LINE 044: format_row関数の定義。1件の計測結果の辞書を受け取るformat_row関数を定義しています。
LINE 045: 関数の役割を説明するドキュメント文字列。1件の計測結果を人間が読みやすい1行の文字列に整える関数であることを説明しています。LINE 046: ステータス未取得の判定。
statusキーがNoneかどうかを調べて、通信エラーが発生したケースかどうかを判定しています。LINE 047: エラー表示ラベルの作成。通信エラーの場合、エラー種別を含めたERR形式のラベル文字列を作成しています。
LINE 048: else節の開始。ステータスコードが取得できている場合の処理をこの行から始めています。LINE 049: ステータスラベルの作成。
取得できたステータスコードを文字列に変換してstatus_labelに格納しています。LINE 050: 成功可否マークの作成。通信が成功していればOK、失敗していればNGという文字列をmarkに格納しています。
LINE 051: 整形済み1行文字列の作成。マーク、ステータスラベル、応答時間、URLを組み合わせて、桁揃えされた見やすい1行の文字列を作成して返しています。RUN 3/7: format_rowで1行に整形できる時点を確認する。
format_rowが定義できた時点です。check_urlの結果を1行の文字列に整形できるか確認します。CHECK 3/7: 途中実行に成功。
[NG] ERR(ConnectionError) 0.8ms http://127.0.0.1:1 LINE 054: 結果表示関数の目的コメント。計測結果一覧とサマリを画面に表示する処理がこれから定義されることを示すコメントです。LINE 055: report関数の定義。
複数の計測結果を受け取るreport関数を定義しています。LINE 056: 関数の役割を説明するドキュメント文字列。結果一覧と正常件数のサマリをecho出力する関数であることを説明しています。
LINE 057: 結果一覧のループ開始。渡された計測結果を1件ずつ取り出すためのforループを開始しています。LINE 058: 整形済み1行の出力。
format_row関数で整形した1件分の結果をclick.echoを使って画面に出力しています。LINE 059: 正常件数の集計。okキーがTrueである結果の件数を数え上げて、正常に応答した件数をok_countに格納しています。
LINE 060: サマリ行の出力。正常件数と全体件数をまとめたサマリメッセージをclick.echoで画面に出力しています。RUN 4/7: reportで結果一覧と件数サマリを表示できる時点を確認する。
reportまで定義できた時点です。複数件の結果を一覧とサマリで表示できるか確認します。CHECK 4/7: 途中実行に成功。
[NG] ERR(ConnectionError) 1.0ms http://127.0.0.1:1 [NG] ERR(ConnectionError) 0.5ms http://127.0.0.1:2 --- 0/2件が正常応答 --- LINE 063: デモ用サーバ生成の目的コメント。
デモ用ローカルサーバのリクエストハンドラを作る処理がこれから定義されることを示すコメントです。LINE 064: _make_handler関数の定義。遅延時間を受け取り、デモ用のリクエストハンドラクラスを組み立てる_make_handler関数を定義しています。
LINE 065: 関数の役割を説明するドキュメント文字列。正常・遅延・エラーの3種類の応答を返すハンドラクラスを組み立てる関数であることを説明しています。LINE 067: デモ用ハンドラクラスの定義開始。
BaseHTTPRequestHandlerを継承した_DemoHandlerクラスの定義を開始しています。ここでリクエストごとの応答内容を実装します。LINE 068: GETリクエスト処理メソッドの定義。
GETリクエストを受け取ったときに呼び出されるdo_GETメソッドを定義しています。LINE 069: okパスの判定。リクエストされたパスが/okかどうかを判定しています。
LINE 070: 正常応答の返却。パスが/okの場合、ステータス200と本文okを返す処理を呼び出しています。LINE 071: slowパスの判定。
リクエストされたパスが/slowかどうかを判定しています。LINE 072: 遅延の発生。指定されたミリ秒数だけ待機し、遅い応答をわざと再現しています。
LINE 073: 遅延後の正常応答の返却。待機後にステータス200と本文slow-okを返す処理を呼び出しています。LINE 074: errorパスの判定。
リクエストされたパスが/errorかどうかを判定しています。LINE 075: サーバエラー応答の返却。パスが/errorの場合、ステータス500と本文server-errorを返す処理を呼び出しています。
LINE 076: 該当なしの場合の分岐。どのパスにも一致しなかった場合の処理をこの行から始めています。LINE 077: 未定義パスへの応答。
未定義のパスに対してステータス404と本文not-foundを返す処理を呼び出しています。LINE 079: 共通レスポンス送信メソッドの定義。HTTPレスポンスを組み立てて送るための共通処理を、_replyという補助メソッドとして定義します。
ステータスコードと本文を引数で受け取り、各エンドポイントの処理から共通で呼び出せるようにします。LINE 080: ステータスコードの送信。send_responseメソッドで、指定されたステータスコードをクライアントに送信します。
これによりレスポンスの先頭行が組み立てられます。LINE 081: Content-Typeヘッダーの設定。レスポンスの種類をtext/plainで、文字コードをUTF-8として伝えるヘッダーを送信します。
クライアント側が本文を正しくテキストとして解釈できるようにします。LINE 082: Content-Lengthヘッダーの設定。本文のバイト長を計算し、Content-Lengthヘッダーとして送信します。
クライアントが受信するデータの終わりを正しく判断できるようにするための情報です。LINE 083: ヘッダー送信の完了。end_headersを呼び出し、ヘッダー部分の送信を終えて本文の送信に移れる状態にします。
ヘッダーと本文の区切りを明示する処理です。LINE 084: レスポンス本文の書き込み。wfile.writeを使って、実際のレスポンス本文をクライアントに送信します。
ここまでの処理でHTTPレスポンス全体の送信が完了します。LINE 086: ログ出力メソッドの上書き定義。サーバーのアクセスログを出力するlog_messageメソッドを上書きして定義します。
デモ実行時に余分なログが表示されないようにするための工夫です。LINE 087: ログ出力を無効化。passを記述することで、本来出力されるはずのアクセスログを何も出力しないようにします。
これによりデモ実行時の標準出力がすっきりします。LINE 089: ハンドラクラスを返す。組み立てたDemoHandlerクラスをそのまま返却します。
呼び出し元ではこのクラスをサーバー起動時のハンドラとして利用します。RUN 5/7: デモ用サーバのハンドラが組み立てられた時点を確認する。_make_handlerまで定義できた時点です。
ハンドラクラスが生成できるか確認します。CHECK 5/7: 途中実行に成功。<class 'lesson._make_handler.<locals>._DemoHandler'> LINE 092: CLI定義部分の見出しコメント。
ここからCLIツールの入り口となるコマンドグループを定義することを示すコメントです。以降のコードでコマンド全体の構成を組み立てていきます。LINE 093: コマンドグループの宣言。
click.groupデコレータを使い、複数のサブコマンドをまとめるグループとしてcli関数を登録します。checkやdemoといったサブコマンドの親になります。LINE 094: CLIグループ関数の定義。
サブコマンドをまとめるための空の関数cliを定義します。この関数自体は処理を持たず、デコレータによりグループの器として機能します。LINE 095: グループの説明文の設定。
docstringとしてツール全体の説明を記述し、ヘルプ表示時にこの文章が案内文として表示されるようにします。利用者がツールの目的を把握できるようにする役割です。LINE 098: checkコマンドの目的説明。
ここからURLを引数で受け取り、まとめて計測するcheckコマンドを定義することを示すコメントです。以降でオプションや処理内容を組み立てていきます。LINE 099: checkコマンドの登録。
cli.commandデコレータを使い、checkという名前のサブコマンドとしてこの関数を登録します。これによりコマンドラインからcheckを呼び出せるようになります。LINE 100: URL引数の定義。
click.argumentで、可変長の引数urlsを受け取れるように設定します。nargsに-1を指定して複数のURLを一度に受け取り、requiredで最低1件の入力を必須にしています。LINE 101: タイムアウトオプションの定義。
click.optionでタイムアウト秒数を指定できるオプションを追加します。デフォルト値は5.0秒とし、ヘルプにもその既定値と説明文が表示されるようにしています。LINE 102: check関数の定義。
urlsとtimeoutを引数として受け取るcheck関数を定義します。この関数がcheckコマンド実行時に呼び出される本体処理になります。LINE 103: checkコマンドの説明文。
docstringとして、このコマンドが1件以上のURLを受け取り、状態と応答時間を計測して表示することを説明します。ヘルプ表示時の案内文として使われます。LINE 104: 計測処理とレポート出力の実行。
check_all関数で全URLの計測結果をまとめて取得し、その結果をreport関数に渡して画面に表示します。checkコマンドの中心的な処理です。RUN 6/7: checkコマンドが公開された時点を確認する。
checkコマンドまで定義できた時点です。CliRunnerで実際に呼び出して出力を確認します。CHECK 6/7: 途中実行に成功。
[NG] ERR(ConnectionError) 1.0ms http://127.0.0.1:1 --- 0/1件が正常応答 --- LINE 107: demoコマンドの目的説明。ここからローカルサーバを立ち上げて実際に計測を行うdemoコマンドを定義することを示すコメントです。サーバーの起動から停止までの一連の流れを実装していきます。
LINE 108: demoコマンドの登録。cli.commandデコレータを使い、demoという名前のサブコマンドとしてこの関数を登録します。これによりコマンドラインからdemoを呼び出せるようになります。
LINE 109: タイムアウトオプションの定義。demoコマンド用にタイムアウト秒数を指定できるオプションを追加します。デフォルトは5.0秒で、ヘルプにも既定値が表示されます。
LINE 110: 遅延時間オプションの定義。slowエンドポイントでの待ち時間をミリ秒単位で指定できるオプションを追加します。オプション名はslow-msですが、関数内ではslow_msという変数名で受け取ります。
LINE 111: 繰り返し回数オプションの定義。計測を何回繰り返すかを指定できるroundsオプションを追加します。デフォルトは1回で、複数回の計測を試したい場合に利用します。
LINE 112: demo関数の定義。timeout、slow_ms、roundsの3つの引数を受け取るdemo関数を定義します。この関数がdemoコマンド実行時に呼び出される本体処理になります。
LINE 113: demoコマンドの説明文。docstringとして、ローカルにデモ用サーバを起動し、正常・遅延・エラーのURLを実測してから停止することを説明します。ヘルプ表示時の案内文として使われます。
LINE 114: デモ用サーバーの起動準備。ThreadingHTTPServerを使い、ローカルホストの空いているポートでサーバーインスタンスを作成します。ハンドラには先ほど作成した_make_handlerの結果を指定しています。
LINE 115: サーバーアドレスの取得。server_addressから実際に割り当てられたホスト名とポート番号を取得します。ポート番号は0を指定していたため、OSが自動的に割り当てた番号がここで確認できます。
LINE 116: サーバー用スレッドの作成。サーバーをバックグラウンドで動かし続けるため、serve_foreverをターゲットとする別スレッドを作成します。daemonをTrueにすることでメインプログラム終了時にスレッドも自動的に終了します。
LINE 117: スレッドの起動。作成したスレッドを開始し、デモ用サーバーが実際に動き出してリクエストを受け付けられる状態にします。これでメイン処理と並行してサーバーが稼働します。
LINE 118: 計測処理のtryブロック開始。サーバー起動後の計測処理を試みるtryブロックを開始します。finallyと組み合わせることで、途中でエラーが起きてもサーバーの停止処理が必ず実行されるようにしています。
LINE 119: ベースURLの組み立て。取得したホストとポート番号を使い、デモ用サーバーへアクセスするためのベースとなるURL文字列を組み立てます。以降の各URLはこの文字列を基に作られます。
LINE 120: 計測対象URLリストの作成。正常応答、遅延応答、エラー応答の3つのエンドポイントに対応するURLをリストとしてまとめます。これがcheck_allに渡される計測対象になります。
LINE 121: サーバー起動メッセージの表示。デモ用サーバーがどのアドレスで起動したかを、click.echoを使って画面に表示します。利用者が今どのURLで計測しているかを把握できるようにします。
LINE 122: 計測ラウンドの繰り返し処理。roundsで指定された回数だけ計測を繰り返すためのforループを開始します。round_noには現在何回目かを表す番号が入ります。
LINE 123: 複数ラウンド時の条件分岐。roundsが1より大きい場合、つまり複数回計測する場合にのみ、ラウンド番号を表示するかどうかを判定します。1回だけの計測では余分な表示を省く工夫です。
LINE 124: ラウンド番号の表示。現在何回目のラウンドかを、click.echoを使って画面に表示します。複数回の計測結果を見やすく区切るための表示です。
LINE 125: 計測とレポート出力の実行。check_all関数で対象URLの計測結果を取得し、report関数に渡して結果を画面に表示します。これがラウンドごとに繰り返される中心処理です。
LINE 126: 後片付け用finallyブロックの開始。tryブロックの処理が終わった後、エラーの有無にかかわらず必ず実行されるfinallyブロックを開始します。ここでサーバーの停止処理をまとめて行います。
LINE 127: サーバー稼働の停止。shutdownメソッドを呼び出し、serve_foreverによるサーバーの稼働ループを止めます。これによりバックグラウンドスレッドの処理が終了に向かいます。
LINE 128: サーバーソケットの解放。server_closeメソッドを呼び出し、サーバーが使用していたソケットを解放します。ポートなどのリソースをきちんと後片付けする処理です。
LINE 129: サーバー停止メッセージの表示。デモ用サーバーを停止したことを、click.echoを使って画面に表示します。利用者に処理が正しく終了したことを伝える役割です。
RUN 7/7: demoコマンドが完成し一連の流れを確認する。demoコマンドまで定義できた時点です。ローカルサーバの起動から停止までの一連の出力を確認します。
CHECK 7/7: 途中実行に成功。
デモ用サーバをhttp://127.0.0.1:40449に起動しました [OK] 200 1.6ms http://127.0.0.1:40449/ok [OK] 200 401.3ms http://127.0.0.1:40449/slow [NG] 500 1.0ms http://127.0.0.1:40449/error --- 2/3件が正常応答 --- デモ用サーバを停止しました LINE 132: 実行部分の見出しコメント。
このファイルをスクリプトとして直接実行した際の入り口部分であることを示すコメントです。以降でCLIの実行処理を呼び出します。LINE 133: 直接実行時の判定。
このファイルが直接実行されたときにのみ真となる条件文です。他のファイルからインポートされた場合には、この中の処理は実行されません。LINE 134: CLIの起動。
cli関数を呼び出し、click.groupで定義したコマンド群を実行可能な状態にします。これによりcheckやdemoといったサブコマンドをコマンドラインから使えるようになります。実行1/4: checkコマンドでローカルURLの状態を計測する。
ローカルに簡易サーバを立てたうえでcheckコマンドを呼び出し、応答が一覧表示されることを確認します。
コード全体は、1件を計測するcheck_url、複数をまとめるcheck_all、結果を整えるformat_row、表示するreportという小さな関数に分けています。役割ごとに分けると、あとで直しやすくなります。
計測の中心はcheck_urlです。GETの前後で時間を測り、成功時はステータスとresponse.okを、失敗時はエラー種別を辞書にまとめて返します。
demo用には_make_handlerでローカルサーバの応答を作り、@click.group()を付けたcliにcheckとdemoをぶら下げました。ここからは特に押さえたい部分を抜き出して見ていきます。
このセクションの用語
- perf_counter
- 高精度な経過時間の計測に向いた時計です。時刻の差を取って処理時間を測れます。
- RequestException
- requestsで起きる通信エラーの基底となる例外です。接続失敗やタイムアウトをまとめて捕まえられます。
- echo
- Clickが用意する出力用の関数です。printに近い使い勝手で、環境ごとの出力の違いを吸収します。
"""ClickとrequestsでURL監視するCLIツール。複数URLの状態と応答時間を確認する。"""
# ライブラリの読み込み
import threading
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import click
import requests
# URL1件の計測
def check_url(url, timeout=5.0):
"""1つのURLへGETし、状態と応答時間をまとめた辞書を返す。"""
started = time.perf_counter()
try:
response = requests.get(url, timeout=timeout)
elapsed_ms = (time.perf_counter() - started) * 1000
return {
"url": url,
"ok": response.ok,
"status": response.status_code,
"elapsed_ms": round(elapsed_ms, 1),
"error": None,
}
except requests.RequestException as exc:
elapsed_ms = (time.perf_counter() - started) * 1000
return {
"url": url,
"ok": False,
"status": None,
"elapsed_ms": round(elapsed_ms, 1),
"error": type(exc).__name__,
}
# 複数URLの一括計測
def check_all(urls, timeout=5.0):
"""複数URLを順番に計測し、結果のリストを返す。"""
return [check_url(url, timeout=timeout) for url in urls]
# 計測結果の整形
def format_row(result):
"""1件の計測結果を人間が読みやすい1行に整える。"""
if result["status"] is None:
status_label = f"ERR({result['error']})"
else:
status_label = str(result["status"])
mark = "OK" if result["ok"] else "NG"
return f"[{mark}] {status_label:>6} {result['elapsed_ms']:>7.1f}ms {result['url']}"
# 計測結果の表示
def report(results):
"""結果一覧と正常件数のサマリをechoで出力する。"""
for result in results:
click.echo(format_row(result))
ok_count = sum(1 for r in results if r["ok"])
click.echo(f"--- {ok_count}/{len(results)} 件が正常応答 ---")
# デモ用ローカルサーバのハンドラ生成
def _make_handler(slow_ms):
"""正常・遅延・エラーの3応答を返すハンドラクラスを組み立てる。"""
class _DemoHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/ok":
self._reply(200, b"ok")
elif self.path == "/slow":
time.sleep(slow_ms / 1000)
self._reply(200, b"slow-ok")
elif self.path == "/error":
self._reply(500, b"server-error")
else:
self._reply(404, b"not-found")
def _reply(self, code, body):
self.send_response(code)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *args):
pass
return _DemoHandler
# CLIのエントリポイント
@click.group()
def cli():
"""複数URLの状態と応答時間を確認するURL監視ツール。"""
# checkコマンド: 引数のURL群を計測
@cli.command()
@click.argument("urls", nargs=-1, required=True)
@click.option("--timeout", default=5.0, show_default=True, help="1件あたりのタイムアウト秒数")
def check(urls, timeout):
"""引数のURL(1件以上必須)の状態と応答時間を計測して表示する。"""
report(check_all(urls, timeout=timeout))
# demoコマンド: ローカルサーバで実測して停止
@cli.command()
@click.option("--timeout", default=5.0, show_default=True, help="1件あたりのタイムアウト秒数")
@click.option("--slow-ms", "slow_ms", default=400, show_default=True, help="遅延エンドポイントの待ち時間(ミリ秒)")
@click.option("--rounds", default=1, show_default=True, help="計測を繰り返す回数")
def demo(timeout, slow_ms, rounds):
"""ローカルにデモ用サーバを起動し、正常・遅延・エラーのURLを実測して停止する。"""
server = ThreadingHTTPServer(("127.0.0.1", 0), _make_handler(slow_ms))
host, port = server.server_address
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
base = f"http://{host}:{port}"
urls = [f"{base}/ok", f"{base}/slow", f"{base}/error"]
click.echo(f"デモ用サーバを {base} に起動しました")
for round_no in range(1, rounds + 1):
if rounds > 1:
click.echo(f"[ラウンド {round_no}/{rounds}]")
report(check_all(urls, timeout=timeout))
finally:
server.shutdown()
server.server_close()
click.echo("デモ用サーバを停止しました")
# スクリプト実行
if __name__ == "__main__":
cli()
コード全文は上の折り畳みに入れてあるので、全部を上から読む必要はありません。ここでは特に重要な部分だけを抜き出して、何をしているのか順番に見ていきます。
check_urlで1件のURLを計測する
def check_url(url, timeout=5.0):
"""1つのURLへGETし、状態と応答時間をまとめた辞書を返す。"""
started = time.perf_counter()
try:
response = requests.get(url, timeout=timeout)計測前にtime.perf_counterで開始時刻を記録し、requests.getでURLへGETを送ります。timeoutは接続や読み取りの待機時間の上限で、応答が来ない時に長く止まらないようにします。
perf_counterで応答時間をミリ秒に直す
elapsed_ms = (time.perf_counter() - started) * 1000
return {
"url": url,
"ok": response.ok,
"status": response.status_code,開始からの経過秒に1000を掛けてミリ秒へ換算します。response.okはHTTP400未満ならTrueになる判定で、status_codeと一緒に辞書へ入れています。
RequestExceptionを握って処理を続ける
except requests.RequestException as exc:
elapsed_ms = (time.perf_counter() - started) * 1000
return {
"url": url,
"ok": False,
"status": None,
"elapsed_ms": round(elapsed_ms, 1),
"error": type(exc).__name__,
}requests側で起きる接続失敗やタイムアウトはRequestExceptionでまとめて捕まえます。エラーでも辞書を返すので、途中で止まらず残りのURLの計測を続けられます。
format_rowで結果を1行に整える
def format_row(result):
"""1件の計測結果を人間が読みやすい1行に整える。"""
if result["status"] is None:
status_label = f"ERR({result['error']})"
else:
status_label = str(result["status"])
mark = "OK" if result["ok"] else "NG"ステータスが取れない失敗時はエラー種別をERR(...)として表示し、成功時はステータス番号を出します。先頭のOKとNGはresult["ok"]で切り替えています。
reportで正常件数のサマリを出す
for result in results:
click.echo(format_row(result))
ok_count = sum(1 for r in results if r["ok"])
click.echo(f"--- {ok_count}/{len(results)} 件が正常応答 ---")各結果をclick.echoで1行ずつ表示し、最後に正常だった件数を数えてまとめを出します。echoはprintに近い出力で、環境ごとの差をClickが吸収してくれます。
_make_handlerでデモ用の3応答を作る
def do_GET(self):
if self.path == "/ok":
self._reply(200, b"ok")
elif self.path == "/slow":
time.sleep(slow_ms / 1000)
self._reply(200, b"slow-ok")
elif self.path == "/error":
self._reply(500, b"server-error")デモ用サーバは/okで正常、/slowでtime.sleepによる遅延、/errorで500を返します。実サイトの代わりにこれらへrequestsでアクセスし、外部通信なしで挙動を試せます。
PythonのClickとrequestsで作ったURL監視CLIの動作確認
作ったツールはdemoコマンドを中心に、合計4回のコマンドで実行しました。いずれも終了コード0で、正常に動作しました。
demoは127.0.0.1の空きポートにローカルサーバを立ち上げ、正常・遅延・エラーの3エンドポイントへrequestsで実測してから必ず停止します。外部通信なしで動くので、手元だけで安全に試せます。
オプションを切り替えながら実行し、--timeoutで待機時間、--slow-msで遅延の大きさ、--roundsで繰り返し回数を変えて挙動を確認しました。
このセクションの用語
- 終了コード
- プログラム終了時にOSへ返す数値です。0は正常終了を表します。
- ポート
- 通信の窓口を表す番号です。空きポートを使うと他と衝突せずにサーバを立てられます。
- エンドポイント
- アクセス先を表す個々のURLです。/okや/errorのように機能ごとに分かれます。
今回キャプチャを取った4つの実行は次のとおりです。
- python subject.py demo(そのまま実行、終了コード0)
- python subject.py demo --timeout 3(待機3秒で実行、終了コード0)
- python subject.py demo --slow-ms 700(遅延700msで実行、終了コード0)
- python subject.py demo --rounds 2(2回繰り返して実行、終了コード0)




PythonのClickとrequests利用時に起きやすいエラーと対処法
初心者がつまずきやすいのは、実行前の準備やURLの書き方に関するエラーです。ここでは代表的なものと対処をまとめました。
なおcheckで接続に失敗した場合は、ツール自身が例外を握ってERR(...)と表示し、処理を止めずに続けます。
このセクションの用語
- MissingSchema
- requestsがURLの通信方式を判別できない時の例外です。http://などが無いと起きます。
- スキーム
- URL先頭のhttpやhttpsの部分です。どの方式で通信するかを表します。
| エラー例 | 原因 | 対処 |
|---|---|---|
| ModuleNotFoundError: No module named 'click' | clickが未インストール | pip install clickで導入する |
| ModuleNotFoundError: No module named 'requests' | requestsが未インストール | pip install requestsで導入する |
| Error: Missing argument 'URLS...' | checkにURLを渡していない | URLを1件以上指定して実行する |
| requests.exceptions.MissingSchema | http://などのスキームがない | URLの先頭にhttp://を付ける |
| requests.exceptions.ConnectTimeout | 接続先が時間内に応答しない | timeoutを延ばすかURLを見直す |
PythonのClickの引数とrequestsのタイムアウト注意点
特に混同しやすいのが、引数の必須指定とタイムアウトの意味です。checkはnargs=-1とrequired=Trueの組み合わせなので、URLは0件では実行できず1件以上が必須になります。
timeoutは接続や読み取りの待機時間の上限であり、処理全体の厳密な制限時間ではありません。遅い応答をどこまで待つかの目安として設定します。
正常判定に使うresponse.okは、200番台だけでなくHTTP400未満で広くTrueになります。公式ドキュメントの説明も、次のとおりです。
ポイントとしては、Clickの引数指定とrequestsの判定の意味を正しく押さえることです。
このセクションの用語
- nargs
- Clickで引数がいくつの値を取るかを決める設定です。-1にすると個数を固定せず複数を受け取れます。
- タイムアウト
- 応答を待つ制限時間です。requestsのtimeoutは接続や読み取りの待機に効きます。
参考:
©requests公式ドキュメントResponse.okReturns True if status_code is less than 400, False if not.
可変長引数:URLは1件以上必須
timeout:待機時間の上限
OK判定基準:400未満で真陽性
URL監視CLIをPython運用で活かす場面
このツールは、URLの生死と応答速度を素早く確かめたい場面で役立ちます。実務でよく出てくる使い方を表にまとめました。
このセクションの用語
- デプロイ
- 作ったプログラムを実際に動く環境へ配置して公開することです。
- API
- プログラム同士がやり取りする窓口です。URL経由で機能を呼び出せます。
| 使える場面 | 具体的な使い方 |
|---|---|
| 自作サイトの死活監視 | 主要ページのURLをcheckに並べ、定期的に実行して状態を確認する |
| デプロイ直後の確認 | リリース後に本番URLをcheckし、応答時間とステータスを点検する |
| 社内APIの応答チェック | エンドポイントを列挙してcheckし、遅い応答を早めに見つける |
| HTTP挙動の学習 | demoで正常・遅延・エラーの応答を再現し、外部通信なしで観察する |
PythonのClickとrequestsで作ったCLIの総括
PythonのClickとrequestsを使い、複数URLの状態と応答時間を確認するCLIツールを作りました。checkで実URLを計測し、demoでローカルサーバを使った動作確認までを1つのコマンドにまとめられます。
実際にdemoコマンドを4回実行し、いずれも終了コード0で正常に動きました。小さな関数に分けたことで、計測・整形・表示の流れを追いやすくなっています。
まずはdemoで挙動をつかみ、次に自分の監視したいURLをcheckへ渡すと、無理なく実運用へ広げられます。
さらに詳しく学べるPython研修の詳細はこちら参考にした一次情報
- ^ Click Documentation (Commands and Groups / Arguments). https://click.palletsprojects.com/en/stable/, (参照26-07-24).
- ^ Requests: Quickstart. https://requests.readthedocs.io/en/latest/user/quickstart/, (参照26-07-24).
- ^ Requests: Timeouts (Advanced Usage). https://requests.readthedocs.io/en/latest/user/advanced/#timeouts, (参照26-07-24).
- ^ Python標準ライブラリhttp.server. https://docs.python.org/3/library/http.server.html, (参照26-07-24).
※内容は執筆時点のものです。ライブラリやサイトの仕様は変わる可能性があるため、公式ドキュメントもあわせてご確認ください。
ITやプログラミングに関するコラム
【Python】FastAPIで料金プラン見積もりシミュレーターを作ってみた
【Python】pandasとmatplotlibで在庫データのABC分析と構成比を可視化してみた
【Python】Flaskで社内FAQをカテゴリ検索できるWebアプリを作ってみた
【Python】argparseでJSON整形・構文検証・キー検索CLIを試してみた
【Python】NumPyとmatplotlibでモンテカルロ法による円周率推定と収束過程の可視化を試してみた
【Python】Playwrightでスクレイピングを試してみた
【CSS】notで複数の件を除外する方法
【Git】remote設定を変更する方法
【VBA】コメントアウトを設定する方法
x86とx64の違いを分かりやすく解説
ITやプログラミングに関するニュース
VercelがAI GatewayにSeedream 5.0 Proを追加、AI SDKのモデル指定で画像生成と編集が可能に
AWSがAmazon LocationのPlaces APIを強化、住所表記の指定と移動手段別の検索が可能に
VercelがトレースにTree・Waterfallビューを追加、ログ画面で処理の階層と所要時間を確認可能に
Googleがエージェント評価の再考を提唱、難易度を情報量で測るDiscovery Benchを解説
Google CloudがCloud Runサンドボックスを公開プレビューで提供、サービスヘルスは一般提供に
Google Cloud EMEAが英国金融の重要第三者に指定、イングランド銀行・PRA・FCAの直接監督下に
AWS DMS Schema ConversionがSQL Serverのオフライン変換に対応、ソースDBへ接続せずスキーマを変換可能に
EC2 G7インスタンスが米国東部(バージニア北部)で利用可能に、G6比でAI推論性能が最大4.6倍
SageMaker HyperPodが継続プロビジョニングでのAMIベース構成に対応、S3のスクリプト管理なしでSlurmクラスターを作成可能に
AWSがEMR on EKSでSparkトラブルシューティングエージェントに対応、失敗ジョブの原因分析を自然言語で依頼可能に
