Webブラウザログイン自動化＋スクリーンショット取得

夜間に自動ログイン→ページ移動→クリック操作→スクリーンショット取得→ログアウトするPGを作成したので、備忘録として記載しておきます。
今回対象となるのはSafie監視カメラのサイトです。
本来であれば有料プランのSafieAPIで色々できるのかもしれませんが、
現在は無料プランのリアルタイムストリーミング機能だけなので、その画面での手順を自動化しました。

# 環境
- MacOS Catalina 10.15.7
- VS Code 1.67.2
- Python 3.7.8
- Selenium 4.2.0
- Google Chrome 102.0.5005.61

# 事前準備
### Chrome Driverのインストール
Chrome＞設定＞Chromeについてを確認し、Chromeのバージョンを把握しておきます。
![Chrome＞設定＞Chromeについて＞Chromeバージョン確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/b3e12014-ed6a-7eee-022f-4876ffb46bf9.png)

ChromeDriverのサイトに飛んで、対応するChromeバージョンのChromeDriverをダウンロードします。
https://chromedriver.chromium.org/downloads
![ChromeDriver＞自分のChromeバージョンのChromeDriverページへ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/c1d1e462-8cb6-b158-265b-f6ac2dc20d91.png)

自分のOSのChromeDriverをダウンロードして解凍します。
![OSに合ったChromeDriverをダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/b19d013e-58c9-8727-f70a-d88510c5a33e.png)

解凍して手に入れたChromeDriverは、**この後作成するPGと同じフォルダに入れておきます。**

### Seleniumのインストール
ターミナルで下記コマンドを打ち込み、Seleniumをインストールします。
```bash
$ pip install selenium
```

# 実装
ChromeDriverと同じ階層にPythonファイルを作成します。
また、スクリーンショットを格納するフォルダも用意します。
例：
/Users/YamadaTaro/Documents/autoCapture（フォルダ）
└**autoCapture.py**
└chromedriver
└screenshot（スクリーンショットを格納するフォルダ）

### コード詳細
<details><summary>コード</summary>

```autoCapture.py

from selenium import webdriver
from selenium.webdriver.chrome import service as ser
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from datetime import datetime
import time

# Chromeオプション
options = Options()
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--disable-notifications')

def start_chrome():
    DRIVER_PATH = 'ChromeDriverの絶対パス'   # ex.'/Users/YamadaTaro/Documents/autoCapture/chromedriver'
    # ブラウザの起動
    chrome_service = ser.Service(executable_path=DRIVER_PATH)
    driver = webdriver.Chrome(service=chrome_service, options=options)
    driver.maximize_window()

    # Webページにアクセスする
    url = 'https://safie.link/app/auth/login?redirectTo=%2Fdevices%2Fcamera' # SafieのログインページのURL
    driver.get(url)

    return driver

def login_safie(driver):
    login_id = 'ログインID' # ex.'loginid'
    login_pw = 'パスワード'  # ex.'password'

    # ログイン情報を入力
    id = driver.find_element(By.NAME, "inputName")
    id.send_keys(login_id)
    password = driver.find_element(By.NAME, "inputPassword1")
    password.send_keys(login_pw)

    time.sleep(1)   # 入力待ち

    # ログインボタンをクリック
    login_button = driver.find_element(By.CLASS_NAME, "login-button")
    login_button.click()
    time.sleep(1)

def get_screenshot(driver, url):
    folder_path = 'スクリーンショットを格納するフォルダの絶対パス'   # ex.'/Users/YamadaTaro/autoCapture/screenshot/'
    current_date = datetime.now()
    capture_date = current_date.strftime("%Y%m%d_%H%M")
    file_name = capture_date + '.png'   # 撮影時刻をファイル名にする

    driver.get(url)

    time.sleep(8)   # 画面ロード待ち

    # キャプチャ撮影
    pray_button = driver.find_element(By.CLASS_NAME, "dummy-overlay")   # カメラ映像の再生ボタンをクリック
    pray_button.click()
    time.sleep(6)   # 再生ボタンが画面から消えるのを待つ

    driver.save_screenshot(folder_path + file_name)

def logout_safie(driver):
    time.sleep(1)

    # アカウントメニューを開く
    user_button = driver.find_element(By.CLASS_NAME, "sf-icon-user")
    user_button.click()

    time.sleep(1)

    # ログアウト
    logout_button = driver.find_element(By.CLASS_NAME, "sf-icon-logout")
    logout_button.click()

    time.sleep(3)


if __name__ == '__main__':
    driver = start_chrome()

    login_safie(driver)

    time.sleep(5)   # 画面ロード待ち

    get_screenshot(driver, '個別カメラ画面のURL')

    time.sleep(3)

    logout_safie(driver)

    driver.close()

```
</details>

ファイルの作成が終わったら、意図した動きをしてくれるかどうか確認してみましょう。

```bash
autoCapture $ python autoCapture.py
```
①自動ログイン
②トップページ表示
③個別カメラ画面遷移
④再生ボタンクリック
⑤画面撮影
⑥ログアウト
の一連の流れができ、screenshotフォルダにスクリーンショットが格納されていれば正常な動作となります。

### 解説
```
id = driver.find_element(By.NAME, "inputName")
```
開発者ツールでログインフォームを確認すると、inputNameというname属性が設定されていることが分かります。
![ログインフォームにname属性inputNameが設定されている.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/61b26931-29a0-c230-b163-d17a1e623d18.png)
ページ内の「name属性=inputName」という要素をfind_elementで検索して特定する仕組みです。
コード内でも記述していますが、name属性だけでなく、クラス属性やID属性なども検索できます。

# cronで定時実行
crontabで夜間の定時実行を設定します。

### シェルスクリプト化
上記のPythonファイルをシェルスクリプトから実行するようにします。

事前にwhichコマンドでPythonのパスを確認しておきます。
```bash
$ which python3
/Library/Frameworks/Python.framework/Versions/3.7/bin/python3 #　一例
```

同じ階層にbashファイルを作成します。
例：
/Users/YamadaTaro/Documents/autoCapture（フォルダ）
└autoCapture.py
└**autoCapture.sh**
└chromedriver
└screenshot（スクリーンショットを格納するフォルダ）

```bash:autoCapture.sh
#!/bin/sh

/Library/Frameworks/Python.framework/Versions/3.7/bin/python3 /Users/YamadaTaro/Documents/autoCapture/autoCapture.py
```

bashファイルからPythonの処理が正常に実行できるか確認しておきます。

```bash
autoCapture $ bash autoCapture.sh
```



### crontabの編集
crontabから、夜間定時実行設定をします。
今回は21時〜翌朝7時の毎時0分に実行するよう設定します。

```bash
$ crontab -e
```
iでINSERTモードに切り替え、下記を追加します。

```bash:crontab
LANG=ja_JP.UTF-8

PATH=/usr/sbin:/usr/bin:/bin

0 21-23,0-7 * * * bash /Users/YamadaTaro/Documents/autoCapture/autoCapture.sh
```

追加し終えたら、ESCボタン→「:wq」を入力して上書き保存しましょう。
また、無事追加できたかも確認します。
```bash
$ crontab -l
```

書き込んだ内容がターミナルに表示されていれば問題ありません。
既定の時間になるとプログラムが実行されます。
スリープ状態だと実行されないので、事前にスリープモードを解除しておくことをおすすめします。

# まとめ
1時間ごとに起きて手動でキャプチャする、という事態を免れたので安心しました。
本来はAutomatorで自動化しようと思っていたのですが、途中でブラウザへのアクセスが弾かれるようになってしまいました。
資料が少なくて原因が分からなかったので、いつかリベンジしたいと思います。

# 参考資料
[Selenium入門 Pythonでブラウザを自動化したい方必読！](https://aiacademy.jp/media/?p=346 "Selenium入門 Pythonでブラウザを自動化したい方必読！")
[python × seleniumでID/PASS入力必要な画面に自動ログインしてみた](https://qiita.com/shota-nekoneko/items/64bbd0c2f534d20e7b77 "python × seleniumでID/PASS入力必要な画面に自動ログインしてみた")
[【Python】Selenium3から4へ上げたらWarningが出た【WebDriver】](https://qiita.com/yagaodekawasu/items/5813a8cb4c3d73386e7a "【Python】Selenium3から4へ上げたらWarningが出た【WebDriver】")