【Selenium】 Raspberry Pi で Firefox を使ってヘッドレス (ディスプレイなし) でフォーム入力を自動化

公開日:
最終更新日:
Selenium でラズパイ上で自動化

毎日同じ時間に同じことをするのはかなりめんどくさいことです.
めんどくさいことは Python にやらせようということで,Selenium を使って自動でフォーム入力ができるようにしていきたいと思います.
この記事では Selenium を使って動くコードが完成している状態 を想定していて,そのコードを常時起動しているラズパイに移植して,自動で動くようにするというところを解説していきたいと思います.
Selenium のインストール方法やコードの書き方は以下の記事に書いているので,そちらをご覧ください.
この記事で作った chrome.pyを元に移植していきたいと思います.

くれぐれも悪用はしないようにしてくださいね.

1. 環境

  • Raspberry Pi 4 Model B / 4GB: armv7l
    • lscpu で確認可能
  • Raspberry Pi OS (Raspbian): 10.9
    • cat /etc/debian_version で確認可能
  • Python: 3.7.3
    • python -V で確認可能
  • Mozilla Firefox: 78.9.0esr
    • firefox --version で確認可能

ちなみに,Firefox を選んだ理由はコマンドだけでインストールしやすいからです.
普段は Google Chrome を使っているのですが,ディスプレイのついていないラズパイではインストールするのが難しくて諦めました.

2. Firefox ESR とドライバのインストール

2.1. Firefox ESR のインストール

$ sudo apt install firefox-esr

2.2 GeckoDriver のインストール

GeckoDriver は色々なバージョンがあるのですが,ARM で動くもののうち最新のものは v0.23.0 なので,これをインストールします.

$ wget https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-arm7hf.tar.gz
$ tar -zxvf geckodriver-v0.23.0-arm7hf.tar.gz
$ sudo mv geckodriver /usr/local/bin
$ rm geckodriver-v0.23.0-arm7hf.tar.gz

3. ヘッドレス化する

これを実現するためには2つ方法があります.
1つは A. Firefox を起動する時に headless オプションをつける で,もう1つは B. Xvfb を使って仮想ディスプレイで起動する です.
特にこだわりがない場合はインストールしたりする必要のない A の方法を取ることをおすすめします.

A. Firefox を起動する時に headless オプションをつける

B の方法で仮想ディスプレイを起動する予定の人はここはスルーして OK です.

まず,Firefox を起動している場所を探します.

driver = webdriver.Firefox()

Chrome でやっていた場合は以下2行のいずれかようなコードになっていると思うので,↑のコードに直しておいてください.

# A の方法でインストールした場合
driver = webdriver.Chrome()
# B の方法でインストールした場合,ドライバを(必要なら)インストールして立ち上げます
driver = webdriver.Chrome(ChromeDriverManager().install())

ついでに Chrome 用に以下のパッケージ等を import している場合,Firefox では必要ないので取り除いて OK です.

import chromedriver_binary
from webdriver_manager.chrome import ChromeDriverManager

次に,Firefox を起動している driver = webdriver.Firefox()より上で以下のようにオプションを設定していきます.

Options = webdriver.FirefoxOptions()
# ヘッドレス化
Options.headless = True
# 必須
Options.add_argument('--no-sandbox')
Options.add_argument('--disable-gpu')
# エラーの許容
Options.add_argument('--ignore-certificate-errors')
Options.add_argument('--allow-running-insecure-content')
Options.add_argument('--disable-web-security')
# headless では不要そうな機能を除外
Options.add_argument('--disable-desktop-notifications')
Options.add_argument("--disable-extensions")
# UA設定 (なくてもいい)
Options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0')
# 言語 (なくてもいいが言語によって表示されるページが変わる可能性があるので念のため)
Options.add_argument('--lang=ja')
# 画像を読み込まないで軽くする
Options.add_argument('--blink-settings=imagesEnabled=false')

これでオプションの設定ができました.
これを反映して,Firefox を起動するために,起動する部分を少し書き換えます.

# import os をしていなければしておく
log_path = os.path.join(os.path.dirname(__file__), 'geckodriver.log')
driver = webdriver.Firefox(options=Options, service_log_path=log_path)

このようにして起動することで,先程のオプションが反映されます.
また,このソースコードを置いているディレクトリと同じディレクトリに geckodriver.log というログファイルが生成されて,エラー内容を記録してくれます.
これで,ヘッドレス化が完了しました.
この部分は以下のサイトを参考にしました.

B. Xvfb を使って仮想ディスプレイで起動する

A の方法で Firefox をヘッドレス化した人はここはスルーして OK です.

Xvfb (X virtual framebuffer) は X Window System の仮想ディスプレイを作るためのバッファを提供することで,GUI アプリケーションを実行できるようにしてくれます.
まずはこれをインストールしておきます.

$ sudo apt install xvfb

さらにそれを起動し,仮想ディスプレイに反映させておきます.

$ Xvfb :1 -screen 0 1024x768x24 &
$ export DISPLAY=:1

ラズパイを再起動した時でも毎回自動で実行されるように .bashrc に登録しておくといいかと思います.

$ echo "Xvfb :1 -screen 0 1024x768x24 &" >> ~/.bashrc
$ echo "export DISPLAY=:1" >> ~/.bashrc

これで,オプションなしで,以下のように起動できます.(ログの場所を指定しなくていいなら,元のままで大丈夫です.)

# import os をしていなければしておく
log_path = os.path.join(os.path.dirname(__file__), 'geckodriver.log')
driver = webdriver.Firefox(service_log_path=log_path)

この部分は以下のサイトを参考にしました.

4. デバッグをする

うまくいくことが多いと思うのですが,万が一失敗した場合デバッグをしないといけません… 😢
筆者が実験で使った https://www.google.com/ ではブラウザやOSによって表示されるページがかなり変わってしまうらしく,バグを踏み抜いてしまいました…
詳しい解決法をここで書くと長くなるので,以下の記事を参考にしてください.

結局どこをどうしたかだけ書いておくと,Google の検索ワード入力欄を探す部分を以下のように変更しました.

# このパスでは動かなかったので変更
# elem = driver.find_element_by_xpath('/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/input')
elem = driver.find_element_by_xpath('/html/body/div[1]/div/div[3]/div[1]/form/div/div/div[1]/input')

これでめでたく動くようになるはずです.

5. 完成品

#1
from time import sleep
from selenium import webdriver
# 不要なのでコメントアウト
# from webdriver_manager.chrome import ChromeDriverManager
# log のパスを設定するために追加
import os

def main():
    try:
        # オプションの設定を追加 (方法B の場合は不要)
        Options = webdriver.FirefoxOptions()
        # ヘッドレス化
        Options.headless = True
        # 必須
        Options.add_argument('--no-sandbox')
        Options.add_argument('--disable-gpu')
        # エラーの許容
        Options.add_argument('--ignore-certificate-errors')
        Options.add_argument('--allow-running-insecure-content')
        Options.add_argument('--disable-web-security')
        # headless では不要そうな機能を除外
        Options.add_argument('--disable-desktop-notifications')
        Options.add_argument("--disable-extensions")
        # UA設定 (なくてもいい)
        Options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0')
        # 言語 (なくてもいいが言語によって表示されるページが変わる可能性があるので念のため)
        Options.add_argument('--lang=ja')
        # 画像を読み込まないで軽くする
        Options.add_argument('--blink-settings=imagesEnabled=false')

        #2
        # Chrome ではなく オプションと log のパスを指定して Firefox を起動するように変更
        # driver = webdriver.Chrome(ChromeDriverManager().install())
        log_path = os.path.join(os.path.dirname(__file__), 'geckodriver.log')
        driver = webdriver.Firefox(options=Options, service_log_path=log_path)
        # 方法B の場合は以下のようにして起動する
        # driver = webdriver.Firefox(service_log_path=log_path)

        driver.implicitly_wait(3)

        page_url = 'https://www.google.com/'
        driver.get(page_url)
        sleep(1)

        #3
        # このパスでは動かなかったので変更
        # elem = driver.find_element_by_xpath('/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/input')
        elem = driver.find_element_by_xpath('/html/body/div[1]/div/div[3]/div[1]/form/div/div/div[1]/input')
        elem.clear()
        elem.send_keys("Qiita")
        elem.submit()
        sleep(1)

        #4
        for elem_h3 in driver.find_elements_by_xpath('//a/h3'):
            elem_a = elem_h3.find_element_by_xpath('..')
            print(elem_h3.text)
            print('#', elem_a.get_attribute('href'))

        #5
        driver.close()

        print("Succeed")
    except Exception:
        # 失敗した場合も閉じておく
        driver.close()
        print("Fail")

if __name__ == "__main__":
    main()

6. 自動実行設定

$ crontab -e

で起動したところに自動実行のための設定を書き込めばオッケーです.
crontab の書き方はググればいろいろ出てくると思います.
例えばこちら.

例えば毎時0分に設定したいということであれば以下のようにすればいいです.

# 分 時 日 月 曜日 コマンド
  0  *  *  *  *  python3 /path/to/firefox.py

ちなみに,一度設定した cron を間違えて消してしまう事件が多発するので,バックアップを取って crontabコマンドに aliasを貼っておくことをお勧めします (筆者も被害者の1人です… 😢).

方法はまた別の記事に書きます.

7. まとめ

このようにして,ディスプレイがない状態でもラズパイ上でヘッドレスで Firefox を起動して操作することができました.
これで快適に過ごせますね 👏

作ったものをブラウザのボタンなどで実装しようとすると ModuleNotFound エラーなどで詰まってしまうかもしれないので,こちらの記事の 3. ModuleNotFoundErrorなどをご覧ください!

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA