HOWTO · Python

Python ピング

このチュートリアルでは、Python でサーバに ping する方法を示します。

このページの内容

PythonからホストがICMPエコー要求に応答するか確認するには、OSの ping コマンドを subprocess.call() または os.system() で実行するか、ping3.ping() 関数を使います。応答があればホストへの到達性や往復時間を確認できますが、特定のアプリケーションやサービスが利用可能であることまでは証明できません。結果はOS、インストール済みのツール、ファイアウォールのルール、ネットワークポリシーにも左右されます。

subprocess.call() を使ってPythonからサーバーにpingを送信する

subprocess.call() はプログラムを実行し、そのプロセスの終了ステータスを返します。コマンドをリストで渡すと、ホスト名からシェルコマンド文字列を組み立てずに済みます。パケット数を指定するオプションはプラットフォームによって異なり、一般的な ping の実装ではWindowsで -n、Unix系OSで -c を使います。

import platform
import shutil
import subprocess


def ping_host(host):
    ping = shutil.which("ping")
    if ping is None:
        return None  # The ping program is not installed or on PATH.

    count_option = "-n" if platform.system() == "Windows" else "-c"
    try:
        status = subprocess.call(
            [ping, count_option, "1", host],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            timeout=5,
        )
    except (OSError, subprocess.TimeoutExpired):
        return None
    return status == 0


result = ping_host("example.com")
if result is None:
    print("ping is unavailable")
else:
    print("host responded" if result else "no successful response")

提供された実行環境での出力例です。出力ブロック内の表示ラベルはバイト単位で保持されるため、英語のままです。

ping is unavailable

出力例のとおり、提供された実行環境では ping を利用できませんでした。インストールされている環境でこの例を使う場合も、ネットワークへのアクセスが必要です。このコードはコマンドの出力を抑制し、プロセスの終了ステータスが0なら True、0以外なら False、プログラムが見つからないかタイムアウトした場合は None を返します。終了ステータスが0以外でも、原因がパケット損失、フィルタリング、名前解決、その他のシステム固有の条件のどれなのかは特定できません。環境に合わせてホスト名とパケット数のオプションを変更してください。

os.system() を使ってPythonからサーバーにpingを送信する

os.system() はコマンド文字列をシェル経由で実行し、プラットフォームごとの形式で符号化されたステータスを返します。subprocess.call() より柔軟性が低く、信頼できないホスト名をコマンドに連結すると、シェルインジェクションにつながるおそれがあります。ユーザー入力を使う場合は、subprocess.call() に引数のリストを渡す方法を優先してください。固定された信頼できるホスト名で os.system() を使う場合は、現在のプラットフォームに合ったパケット数のオプションを選びます。

import os
import platform
import shutil

host = "example.com"  # Keep this fixed or validate it before use.
ping = shutil.which("ping")
if ping is None:
    print("ping is unavailable")
else:
    count_option = "-n" if platform.system() == "Windows" else "-c"
    status = os.system(f'"{ping}" {count_option} 1 {host} > "{os.devnull}" 2>&1')
    print("host responded" if status == 0 else "no successful response")

提供された実行環境での出力例です。出力ブロック内の表示ラベルはバイト単位で保持されるため、英語のままです。

ping is unavailable

出力例のとおり、提供された実行環境では ping を利用できませんでした。利用可能な環境でも、この例の動作はローカルのシェル、ping の実装、ネットワークに依存します。実行ファイルのパスを引用符で囲むと、パスに空白が含まれる場合に対応できますが、シェルコマンド文字列は引き続き慎重に扱う必要があります。host に任意のユーザー入力を代入しないでください。返されるステータスが示すのはシェルコマンドの結果であり、Webサーバーなどのサービスが接続を受け付けているかどうかではありません。

ping3.ping() 関数を使ってPythonからサーバーにpingを送信する

3つ目の方法は ping3.ping() 関数です。システムの ping コマンドを呼び出さずに、ICMPエコー要求を送信します。スクリプトの実行に使うPython環境へ、python -m pip install ping3 で ping3 パッケージをインストールしてください。OSや権限によっては、ICMPパケットの送信に追加の権限が必要です。

try:
    from ping3 import ping
except ImportError:
    print("Install ping3 with: python -m pip install ping3")
else:
    host = "example.com"
    response = ping(host)
    if response is None or response is False:
        print("no response reported")
    else:
        print(f"round-trip time: {response} seconds")

提供された実行環境での出力例です。出力ブロック内の表示ラベルはバイト単位で保持されるため、英語のままです。

Install ping3 with: python -m pip install ping3

出力例のとおり、提供された実行環境には ping3 がインストールされていませんでした。インストール後も、この例の実行には適切なICMP権限とネットワークアクセスが必要です。数値の応答は、往復時間を秒単位で表します。応答がない場合の意味は、インストールしたライブラリの動作と実行環境に応じて解釈してください。ホストに到達できない場合と、パケットがフィルタリングまたは損失した場合を区別できると決めつけないでください。

安全に引数を渡し、システムに用意された ping ユーティリティを使いたい場合は subprocess.call() を選びます。os.system() は、固定された信頼できるコマンドを実行するための、より短いシェルベースの方法です。ping3.ping() ならPython内で呼び出せて応答時間を取得できますが、パッケージの追加と権限の考慮が必要です。いずれの方法でも確認できるのはICMPによる到達性です。特定のサービスが動作しているか確認するには、アプリケーションレベルのチェックを使ってください。