GrafanaダッシュボードのスクリーンショットをSeleniumで撮る

# はじめに

Grafanaダッシュボードで記録を取っておきたいことが最近多いです。ダッシュボード上のメトリクスをスクリーンショットの形で残しておきたいです。
ただ、色々操作が面倒で自動化したくなり、Seleniumを使うことにしました。

# 経緯

Grafanaダッシュボードで見ていたメトリクスを後で見返したいです。ただ、データをずっと持っておくと、お金がかかってしまうので、スクリーンショットの形で残しておきます。
実際にスクリーンショットを撮ってみると結構面倒です。

* ブラウザにプラグインは入れたくない
* Grafanaにログインしないといけない
* 開発者ツールで撮ろうとしてもスクロールが効かない
* 画面に映さないとグラフが更新されない
* ダッシュボードは外部からインポートしたものもあり複数ある

↑のようなことを思い、じゃあ慣れていなくともSeleniumを使って自動化した方が楽だなと決意しました。

# （参考）開発者ツールを使ったスクリーンショットを撮る方法

Seleniumについて触れる前に、今までの方法を記載します。

まず↓のような画面からGrafanaにログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474054/d2ec6736-c686-f521-6c67-7bbfef131d9b.png)

次にスクリーンショットを撮りたいダッシュボードに移動します。
↓のようにダッシュボード上のメニューから、記録を取りたい時間帯を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474054/937f9f1f-06e0-f97a-7aa1-89bd7be7f0de.png)

その後、F12キーを押して、開発者ツールを起動します。
開発者ツールにてスマホサイトでの表示に切り替えます。

![231019_smartphone_site.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474054/12fa9f54-84ae-3f3d-766a-48d10f4c73c2.png)

表示された画面にダッシュボード全体が表示されるように、サイズを調整します。
その後、フルサイズでスクリーンショットを撮ります。

![231019_test_fullscreen2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474054/4015374c-55b7-9cec-628c-09b180523e5a.png)

:::note warn
注意
スマホサイトで見切れている個所は、スクリーンショットの画像上でも見切れてしまいます。他ウェブサイトではそうはならないんですが。。
それと、スクロールしてみるとわかりますが、ダッシュボード上の各グラフは画面に映ると更新が始まります。
そのため、スマホサイトでダッシュボード全体が映るようにします。
:::

大変だなあと思うので、Seleniumで自動化します。

# Seleniumをインストールする

まずはSeleniumを使えるようにします。

環境は以下の通り。
WSL上でPythonからSeleniumを動かします。ブラウザはFirefoxを使用しています。

```bash
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
$ python3 --version
Python 3.8.2
$ pip list | grep selenium
selenium               4.14.0
$ apt list --installed | grep firefox

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

firefox-geckodriver/focal-updates,focal-security,now 118.0.2+build2-0ubuntu0.20.04.1 amd64 [installed]
firefox/focal-updates,focal-security,now 118.0.2+build2-0ubuntu0.20.04.1 amd64 [installed,automatic]
```

WSLとPythonは実行できるものとして、↓コマンドで他インストールします。
（バージョンはあまり気にしていませんが、調べてみるとスクリプトの書き方も変わるので気にした方がいいですね。。）

```bash
$ python3 -m pip install selenium
$ sudo apt-get install firefox-geckodriver
```

# 使用するPythonスクリプトの内容

以下のPythonファイルを作成しました。

```python:take-screenshot.py
import time

from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

def start_browser():
    options = Options()
    options.add_argument("--headless")
    options.log.level = 'trace'

    profile = webdriver.FirefoxProfile()
    profile.accept_untrusted_certs = True
    options.profile = profile

    browser = webdriver.Firefox(options=options)
    browser.implicitly_wait(10)
    return browser

def login(browser, url):
    print("login ...")
    browser.get(url)
    username = WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.NAME, 'user')))
    username.send_keys("admin")
    password = WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.NAME, 'password')))
    password.send_keys("prom-operator")
    login_btn = WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.XPATH, '//*[@id="reactRoot"]/div/main/div[3]/div/div[2]/div/div/form/button')))
    login_btn.click()

def take_screenshot(browser, url, time_wait, size_x, size_y, output_file):
    print("screenshot {} ...".format(url))
    browser.get(url)
    browser.set_window_size(size_x, size_y)
    time.sleep(time_wait)
    browser.save_screenshot(output_file)
    
def main():
    browser = start_browser()
    login(browser, "https://＜Grafanaのホスト名＞/login")
    with open('list.txt') as f:
        for line in f:
            line = line.replace("\n", "")
            url = line.split(" ")[0]
            time_wait = float(line.split(" ")[1])
            size_x = int(line.split(" ")[2])
            size_y = int(line.split(" ")[3])
            output_file = line.split(" ")[4]
            take_screenshot(browser, url, time_wait, size_x, size_y, output_file)
    browser.quit()

if __name__ == '__main__':
    main()
```

スクリプトでは、Grafanaにログインして、list.txtファイルに書いた設定を基にスクリーンショットを撮るようにしています。
list.txtファイルには以下のように記載します。

```tex:list.txt（例）
https://＜ダッシュボードURL1＞ 20 1850 1870 screenshot1.png
https://＜ダッシュボードURL2＞ 20 1850 3400 screenshot2.png
```

1行毎の項目は以下の通りにしました。

|No|項目|説明|
|---|---|---|
|1|ダッシュボードのURL|スクリーンショットを撮るダッシュボードのURLです。Grafanaでは変数の値もURLに含まれるため、併せたURLを指定できます。|
|2|描画待ち時間|ダッシュボード上のグラフが描画されるまでの待ち時間です。time.sleepを使ってしまってますが、他にいい方法があるかもしれません。。|
|3|画像横サイズ|スクリーンショットの幅を指定します。ウィンドウサイズによりグラフの見え方も変わるので見やすいサイズを指定します。開発者ツールのスマホサイトで見てみるとよいと思います。|
|4|画面縦サイズ|スクリーンショットの幅を指定します。|
|5|画像ファイル名|出力する画像ファイル名を指定します。|

以下のようにpythonを実行します。

```bash
$ python3 take-screenshot.py
# list.txtがあるディレクトリで実行します。
```

# おわりに

今回は久しぶりにSeleniumに触りました。小さな自動化ですが、気にすることが少なくなるとその分、別のことに意識を割けてより楽しく働けそうです。
これからも勉強します！