WEBブラウザの動作をコードで自動化するぞ!
(そんでもってテストに供します)
僕らが生まれてくるずっとずっと前にはもうアポロ11号は月に行ったって言うのと同じように、流行りの RPAツールが世に出てくるずっとずっと前にはもう SeleniumがWEBブラウザを自動運転していたのでした。
Seleniumはブラウザを自動化します。そうです!
その力で何をするかは完全にあなた次第です。
(原文ママ)
Seleniumに初めて触れたのは、Ver.2の頃で、もうすぐ Ver.3が出るよ〜って頃でした。
いよいよ Ver.3が出て「これからは Seleniumで自動テストの時代じゃろ!皆が寝ている夜にテストさせれば捗るわ〜。情弱はいつまでも手動でテストしているがよい!」とイキっていた所、Firefoxの仕様が大幅に変わり Seleniumが動かなくなっちゃったのでした。
当時すごくガッカリしたのを覚えています。
ただ、その後各WEBブラウザが同じ規格を取り始めたこと、問題児だった IEがサポート終了で消えたことで、改めて Seleniumに触れてみようと思った次第です。
Seleniumは各種開発言語を使って、各種 WEBブラウザでのブラウズ作業を自動実行するためのライブラリで、テストの目的が主ですが実務で RPA的に使う人もいるようです。
開発言語は、Java・Python・C#・Ruby・JavaScript・Kotlinとなっており、これだけカバーすればだれでもどれか 1つくらいはやってるだろという感じ。
対応WEBブラウザは、Chromium/Chrome・Firefox・Edge・IE・Safariとなっており、まぁ十分じゃないかなと思います。
では実際に動かしてみることにします。
サーバー環境の構築でも何でもないですが。
1.環境
以下のセットでいきます。
2024年6月23日時点での最新です。
- Ubuntu Desktop 24.04
- Python 3.12.3
- (snap版)Firefox 127.0.1
- Selenium 4.22.0
Ubuntu Desktopにつき、実際の環境は私の趣味で Lubuntu 24.04を使っていますが、他のフレーバーでも変わらんと思います。
2.インストール
Ubuntu 24.04では、インストールするのは Pythonのモジュールだけです。
Pythonのライブラリ管理をする pipコマンドを使いますので、これがインストールされていない場合は先にインストールしておきます。
subro@Lubuntu2404:~$ sudo apt install python3-pip
〜〜〜 省略 〜〜〜
Python用の WebDriver(Pythonのモジュールとして提供されている)をインストールします。
このページの Pythonタブにある方法でやります。
Seleniumライブラリのインストール
subro@Lubuntu2404:~$ pip3 install selenium
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.
See /usr/share/doc/python3.12/README.venv for more information.
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
手順通りにやるとエラーになってしまいました。
サードパーティー製の外部モジュール利用で競合とかの影響が出てきてしまったんでしょう。
Debianが作った Pythonのパッケージを利用している人には、外部モジュール利用は仮想的に分離された環境の利用をさせたいようです。
そしてこのメッセージを読むと pipxを使うのが一番楽とありますので、これをインストールしてみます。
subro@Lubuntu2404:~$ sudo apt install -y pipx
〜〜〜 省略 〜〜〜
入りました。
では pipxコマンドで Seleniumのモジュールをインストールします。
--include-depsオプションは、seleniumが依存する他のモジュールも一緒にインストールさせるためのもので、どんなモジュールを入れるにせよ、基本的にこれを使わないと動かないんじゃないかと思います。
subro@Lubuntu2404:~$ pipx install --include-deps selenium
installed package selenium 4.22.0, installed using Python 3.12.3
These apps are now globally available
- wsdump
⚠ Note: '/home/subro/.local/bin' is not on your PATH environment variable. These apps will not be globally accessible until your PATH is updated. Run `pipx ensurepath` to automatically add it,
or manually modify your PATH in your shell's config file (i.e. ~/.bashrc).
done! ✨ 🌟 ✨
インストールできました。
しかしここに出てきた「Note:」を読むと、[~/.local/bin]ディレクトリにパスが通ってないとダメよとありますね。
pipxコマンドを使うと [~/.bashrc]ファイルに [PATH]環境変数をよろしく設定してくれるようなのでやってみましょう。
subro@Lubuntu2404:~$ pipx ensurepath
Success! Added /home/subro/.local/bin to the PATH environment variable.
Consider adding shell completions for pipx. Run 'pipx completions' for instructions.
You will need to open a new terminal or re-login for the PATH changes to take effect.
Otherwise pipx is ready to go! ✨ 🌟 ✨
上手くいったようです。
[~/.bashrc]ファイルの設定を今使ってるターミナルで有効にします。
subro@Lubuntu2404:~$ . ~/.bashrc
subro@Lubuntu2404:~$ env | grep PATH
XDG_SESSION_PATH=/org/freedesktop/DisplayManager/Session1
XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/subro/.local/bin
[PATH]環境変数に追加されていることも確認できました。
pipxコマンドでインストールしたモジュールの情報はこうすると確認できるようです。
subro@Lubuntu2404:~$ python3 -m pipx list
venvs are in /home/subro/.local/share/pipx/venvs
apps are exposed on your $PATH at /home/subro/.local/bin
manual pages are exposed at /home/subro/.local/share/man
package selenium 4.22.0, installed using Python 3.12.3
- wsdump
これでやっとこ環境ができました。
3.自動実行プログラムを実行してみる
pipxコマンドで作った仮想的に分離された環境で Seleniumを使うには、ここにある pythonコマンドを使えば良い模様です。
subro@Lubuntu2404:~$ ls -l /home/subro/.local/share/pipx/venvs/selenium/bin
合計 28
-rw-r--r-- 1 subro subro 9033 6月 23 06:50 Activate.ps1
-rw-r--r-- 1 subro subro 2084 6月 23 06:50 activate
-rw-r--r-- 1 subro subro 946 6月 23 06:50 activate.csh
-rw-r--r-- 1 subro subro 2225 6月 23 06:50 activate.fish
lrwxrwxrwx 1 subro subro 7 6月 23 06:50 python -> python3
lrwxrwxrwx 1 subro subro 16 6月 23 06:50 python3 -> /usr/bin/python3
lrwxrwxrwx 1 subro subro 7 6月 23 06:50 python3.12 -> python3
-rwxrwxr-x 1 subro subro 255 6月 23 06:50 wsdump
シンボリックリンクの先は [/usr/bin/python3]コマンドですけど、このシンボリックリンクから実行されているというのが大事なのかな?
実際に自動実行を何かやってみます。
このホームページのトップページにアクセスして、タイトルが「お金をかけずにサーバーの勉強をしよう」になってるか確認して、証跡としてトップページの絵を画像ファイル [screenshot.png] として作成するプログラムです。
コードはオフィシャルの例をベースにしていますが、ここで snap版 Firefoxの弊害が出ます。
Firefoxを外部から操作するためのプログラムは Geckodriverと言うのですが、Firefoxも Geckodriverも snap版はサンドボックス環境というちょっと特殊な隔離環境で動いています。
そのため一般に説明されている Seleniumのやり方では上手く Firefoxや Geckodriver自体を見つけられなかったり、Firefoxのプロファイルのファイルを作れなかったりします。
これについてはいくつかの回避策が考案されていて、今回は以下の情報をパクってやりました。
Firefox browser initialization fails with Snap-installed Firefox and Selenium Python version >= 4.11
Selenium geckodriver: profile missing: your firefox profile cannot be loaded
[test.py]
import unittest
import selenium.webdriver
import selenium.webdriver.firefox.service
class OkaneKakenai(unittest.TestCase):
def setUp(self):
firefox_bin = "/snap/firefox/current/usr/lib/firefox/firefox"
firefoxdriver_bin = "/snap/firefox/current/usr/lib/firefox/geckodriver"
options = selenium.webdriver.firefox.options.Options()
#options.add_argument('--headless')
options.binary_location = firefox_bin
service = selenium.webdriver.firefox.service.Service(executable_path = firefoxdriver_bin)
self.driver = selenium.webdriver.Firefox(service = service)
def test_search_in_python_org(self):
driver = self.driver
driver.get("https://subro.mokuren.ne.jp")
self.assertIn("お金をかけずにサーバーの勉強をしよう", driver.title, '---理由--- タイトルが間違っています。')
driver.get_full_page_screenshot_as_file("screenshot.png")
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
Pythonに慣れてる人であれば、Seleniumを知らなくても概ね何をしているか分かると思います。
そして「Firefoxのプロファイルを見つけられなくなる問題の回避策として、[TMPDIR]環境変数を設定し、(snap版だとファイルの書き込みの場所を制限されているので)ホームディレクトリ以下に [tmp]ディレクトリを作ってから [test.py] を実行します。
subro@Lubuntu2404:~/work/python/selenium$ export TMPDIR=~/tmp
subro@Lubuntu2404:~/work/python/selenium$ mkdir ~/tmp
subro@Lubuntu2404:~/work/python/selenium$ /home/subro/.local/share/pipx/venvs/selenium/bin/python test.py
..
----------------------------------------------------------------------
Ran 1 test in 8.270s
OK
結果はテストOKでして、ターミナルには上記の出力と縦に長〜い pngファイルを得られました。
テスト中にモニタを見ていると、自動で Firefoxが立ち上がって、このホームページが表示されていました。
それを観ているのは中々面白いもので、画面が勝手に動いていくのはスクリプトで APIを叩いてるのとはまた違った趣があります。
WebDriverでは、画面にある要素を特定して、入力フィールドなら文字を打ち込み、ボタンがあれば押す、そんな動作を一通りできるようになっていますので、マクロを組む感じで自動化/テストスクリプトを書けると思います。
当HPはサーバー環境のネタを書くために作っているので、このネタはちょっとそこからは外れていますので、この辺で終わりにします。
==========
Seleniumにはリモートノードにある WebDriverに仕事をさせるという機能(Selenium Grid)がありますので、その環境を作るネタをいつか書きたいと思います。
なんか今回は Seleniumで自動化を実現するより、Pythonのモジュール管理に pipxコマンドを使わされるとか、snap版 Firefoxの問題とかの方がずっとお勉強になってしまいました。
日本で流通している Seleniumの本は少なくて、これが最後だったんじゃないかと思います。
2017年刊行ですから最新バージョンではかなり変わってしまっているはずで、あくまで Seleniumの考え方を捉えるくらいですね。
オライリーのもあるんですが、もっと古いです。