Raspberry Pi × Seleniumで定型作業を自動化する

:::note info
この記事はZOZO Advent Calendar 2023 シリーズ9の14日目の記事です。
:::

## はじめに

みなさんは日課にしているもの、ありますか？ 日課には自分の意思でやりたいものと、やりたくないけどやらなくちゃいけないものがあると思います。
やりたくないけどやらなくちゃいけないもの、自動化しませんか？ Raspberry Piは色々な用途の自動化に使えることはみなさんご存知の通りかと思いますが、自分もラズパイを使って定型作業を自動化したので、その事例を紹介します。

今回自動化を行なったのは、主にバックアップの目的でやっていた以下のような定型作業です。
- 30分に1回、以下の作業を行う
    - データベースとして使用しているS3から画像をダウンロードしてくる
    - その画像を自作のWebサイトにアップロードする

上記の手順をラズパイ単体で行うことで、ラズパイを定型作業botに変身させました。
具体的に手順を見てみましょう（ラズパイの初期設定は終わっていることを前提とします）。

---

**1\. Pythonファイルを作成**

まずは特定の処理を定期実行するためのpythonファイルを作成します。
```
touch random-image-upload.py
```

**2\. Selenium・ドライバのインストール**

今回は特定のWebサイトに画像を投稿するため、Webブラウザの操作を自動化できるSeleniumというフレームワークを使用します。
SeleniumをRaspberry Piにインストールする方法は参考になる記事がたくさんあります。以下を参考にしながらインストールを行いました。
- [Raspberry Piでブラウザを自動操作してみる 【Python】](https://irohaplat.com/raspberry-pi-selenium-installation/)

```
# seleniumをインストール
pip install selenium

# chromiumを動かすためのドライバをインストール
sudo apt install chromium-chromedriver

# ドライバのパスを確認 （特に設定をいじっていない場合、/usr/bin/chromedriverになるはず）
which chromedriver

# chromiumのバージョンを更新
sudo apt-get dist-upgrade chromium-browser
```

**3\. AWS CLIのインストール**

AWS S3上にある画像をダウンロードするため、Python上でAWSのCLIを使えるようにします。
```
# AWS CLIをインストール
pip install awscli

# AWS CLI使用設定
aws configure
```
`aws configure` を行うと、設定の入力画面になります。
AWSでRaspberry Pi用のユーザーを作成し、AWS Access Key IDとAWS Secret Access Keyを取得しておき、入力画面でそれらを入力します。

```
aws configure
AWS Access Key ID [None]: AWSAccessKeyId=xxxxxxxxxxxxxxxxxxxx
AWS Secret Access Key [None]: xxxxxxxxx/xxxxxxxxxxxxxxxxx/xxxxxxxxxxxxx
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

**4\. 定期実行する処理を記載する**

```python:random-image-upload.py
import time
import chromedriver_binary
import glob
import boto3
import random
import os
from selenium import webdriver
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager

path= '/usr/bin/chromedriver'
options = ChromeOptions()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
driver = Chrome(service=ChromeService(path), options=options)

# S3に保存されている画像からダウンロードする（今回はランダムで1つ）
def get_random_image():
  s3 = boto3.resource('s3')
  bucket = s3.Bucket('#{BUCKET_NAME}')
  keys = [obj.key for obj in bucket.objects.all()]
  random_key = random.choice(keys)
  # local_pathにダウンロードした画像を保存する
  # すでに存在する画像ファイルに対して行うと画像が上書き保存される
  local_path = os.path.expanduser("~/Develop/img/image.png")
  bucket.download_file(random_key, local_path)

# Webサイトにログインする
def login(account, password):
  # ログインページを開く
  driver.get('https://#{my_service}/login/')
  # アカウントIDとパスワードを入力しEnterを押す
  # HTMLのname属性からIDとpassの要素を取得しログイン情報を入力
  element_account = driver.find_element(By.NAME, 'id')
  element_account.send_keys(account)
  element_pass = driver.find_element(By.NAME, 'password')
  element_pass.send_keys(password)
  
  element_account.send_keys(Keys.RETURN)  

# 画像をアップロードする
def upload_image():
    # S3からダウンロードしてきた画像の保存先を指定する
    image_file_path = "/home/raspberry/Develop/img/image.png"
    # 画像をドロップするところができる要素を見つけ、そこに画像ファイルを貼り付ける
    image_button = driver.find_element(By.XPATH, '//input[@type="file"]')
    image_button.send_keys(image_file_path)

    # アップロードボタンを取得しクリック
    upload_button = driver.find_element(By.XPATH, '//*[@data-testid="upload_button"]')
    element_login.click()

get_random_image()
login('#{USER_ID}', '#{PASSWORD}')
upload_image()
driver.quit()
```

**5\. crontabで定期実行を行うよう設定する**

一度上記のpythonファイルを実行し、無事にポストに成功したら最後に定期的にポストを行うための設定を行います。
以下の記事を参考に設定しました。
- [Raspberry Piでcronを使ってPythonを定期実行する](https://www.raspberrypirulo.net/entry/cron)
```
# cronの設定ファイルを開く
crontab -e

# 毎時29分と59分にrandom-image-upload.pyを実行する
59 * * * * /usr/bin/python3 /home/raspberry/Desktop/random-image-upload.py > /tmp/exec.log 2>&1
29 * * * * /usr/bin/python3 /home/raspberry/Desktop/random-image-upload.py > /tmp/exec.log 2>&1
```

これで無事、Webサイトに画像の投稿を自動化できるようになりました 🎉

## おわりに

今回の自動化の例はかなり汎用的な例として記載しました。いろいろなところで活用できるので、参考になれば幸いです。
（注意: Seleniumを使用したWebサイトのスクレイピングは、サイトの規約によって禁止されていたりすることもあります。自動化を行う場合は規約もしっかり確認し、自己責任で。）