PythonでWiFiの速度計測をする

この記事は [JSL \(日本システム技研\) Advent Calendar 2019](https://qiita.com/advent-calendar/2019/jsl) の13日目の記事です。 

---

最近、社内のWiFiが遅くなるタイミングがあるような気がしていて、とはいえどの程度遅くなっているか定量的に分からなかったため、スピードテストを定期実行するスクリプトを書いてみました。

## 最初にまとめ

- [Speedtest CLI \- Internet connection measurement for developers](https://www.speedtest.net/apps/cli) を使って社内WiFiの速度計測するプログラムをPythonで書いた
- CUIからスピードテスト実行できると便利

## CUIからスピードテスト

まず、cron等で定期的に実行したかったためCUIでスピードテストを実行することを考えました

私のイメージではスピードテストといえば以下のように検索→実行…のようにして計測するものでした。

![スクリーンショット 2019-12-23 15.54.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67790/56eb06ed-3ae9-c779-a0ab-b0cd42fab504.png)

[Linux上でネットワーク回線速度を計測する手段について整理してみた ｜ Developers\.IO](https://dev.classmethod.jp/server-side/network/measuring-network-speedtest-on-linux/)) の記事などをみつつ調査したところ `speedtest.net` がCLIを提供しているようでした

- [Speedtest CLI \- Internet connection measurement for developers](https://www.speedtest.net/apps/cli)

また、Speedtest CLIはPython packageも提供しており、

- pipでbinaryをinstallすることが可能
  - brewで管理しなくても仮想環境化でspeedtestコマンドが使えそう
- Python上でimportすることもできそう

なことがわかりました

## speedtest実行環境の構築

pipenvを使って環境構築しました

```sh
$ mkdir wifi_speedtest
$ cd $_  
# pipenv 未インストールの場合
# brew install pipenv

# 仮想環境作成
$ pipenv --python 3.7

# パッケージのインストール
$ pipenv install speedtest-cli

# 仮想環境の有効化
$ pipenv shell

# speedtestコマンドのテスト
(.venv) $ speedtest --version
speedtest-cli 2.1.2
Python 3.7.4 (default, Oct 12 2019, 18:55:28) [Clang 11.0.0 (clang-1100.0.33.8)]
```

## CUI上でSpeedtestを実行してみる

`speedtest --list` で有志が提供している接続先サーバの一覧が取れるのでいずれかに決めて番号を控えておきます。

```
(.venv) $ speedtest --list | grep Tokyo
15047) OPEN Project (via 20G SINET) (Tokyo, Japan) [6.34 km]
24333) Rakuten Mobile , Inc (Tokyo, Japan) [6.34 km]
28910) fdcservers.net (Tokyo, Japan) [6.34 km]
18516) GIAM PING VIETPN.COM (Tokyo, Japan) [6.34 km]
22247) Tokyonet (Castro, Brazil) [18486.74 km]
```

以下のように指定すると常に指定したサーバでスピードテストを実行します。例えば、 `Rakuten Mobile , Inc` 提供のサーバの場合、番号は `24333` となります。

```
# スピードテストを実行
(.venv) $ speedtest --server 24333
Retrieving speedtest.net configuration...
Testing from XXX (xx.xx.xx.xx)...
Retrieving speedtest.net server list...
Retrieving information for the selected server...
Hosted by Rakuten Mobile , Inc (Tokyo) [6.34 km]: 200.123 ms
Testing download speed................................................................................
Download: 16.08 Mbit/s
Testing upload speed......................................................................................................
Upload: 31.96 Mbit/s
```

無事計測できました(クライアント情報は一部隠しています)

- Download: 16.08 Mbit/s
- Upload: 31.96 Mbit/s

ただ、これだとプログラム上で扱いづらいので `--json` を付与して json 形式で結果を取得してみます

```sh
(.venv) $ speedtest --server 24333 --json
```

```json:結果
{"download": 20208058.464686207, "upload": 54426180.687909536, "ping": 48.215, "server": {"url": "http://ookla.mbspeed.net:8080/speedtest/upload.php", "lat": "35.6833", "lon": "139.6833", "name": "Tokyo", "country": "Japan", "cc": "JP", "sponsor": "Rakuten Mobile , Inc", "id": "24333", "host": "ookla.mbspeed.net:8080", "d": 6.336536019993832, "latency": 48.215}, "timestamp": "2019-12-23T07:23:16.316637Z", "bytes_sent": 68534272, "bytes_received": 25353712, "share": null, "client": {"ip": "xx.xx.xx.xx", "lat": "xx.xxxx", "lon": "xxx.xxxx", "isp": "XXX", "isprating": "3.7", "rating": "0", "ispdlavg": "0", "ispulavg": "0", "loggedin": "0", "country": "JP"}}
```

これをPython上で扱うことを考えます

## PythonでSpeedtestを実行する

せっかくPython Packageとして提供しているので、Pythonの中でプラグラマブルにスピードテストを実行することも考えましたが、今回はCUIでの実行結果をそのまま扱うことを考えたので `subprocessモジュール` でspeedtestコマンドを実行することにしました

[subprocess \-\-\- サブプロセス管理 — Python 3\.8\.1 ドキュメント](https://docs.python.org/ja/3/library/subprocess.html)

```py:wifi_speedtest.py
def get_speedtest_result():
    process = subprocess.run(['speedtest', '--server', '24333', '--json'], capture_output=True)
    return json.loads(process.stdout)


def bit_to_mbit(bit):
    """
    誤差とかは気にしない感じの作り
    """
    return bit / 1024 / 1024


result = get_speedtest_result()
print(bit_to_mbit(result["download"]), bit_to_mbit(result["upload"]))
```

```sh:結果
13.46394215 50.48002296
```

`subprocess` でコマンド実行する方法はいくつかありますが、今回は公式で使用例に載っている `run()` + `capture_output=True` で標準出力を取得しました

今回はSpreadSheetのAPIを叩いて記録するようにしました(詳細は割愛 以下を大いに参考にさせていただきました)。

- [PythonでGoogleスプレッドシートを編集 \- Qiita](https://qiita.com/akabei/items/0eac37cb852ad476c6b9)

Slackに飛ばすなり好きな方法で記録すると良いと思います。

---

余談ですが、Pipenvのscriptsを活用すると、cronから実行するときの仮想環境の有効化などが不要で便利でした(pipenv run経由で実行したプロセスは仮想環境化での実行と同じ扱いになる)

```toml:Pipfile
[scripts]
dev = 'python wifi_speedtest.py'
```