AArch64(arm64)のLinuxで(CUIでheadlessで)Seleniumを動かそうと思ったら
ひと手間必要だったのでまとめました。
この記事でのAArch64はすべてLinuxを表します。
厳密にはApple SiliconのmacOSもAArch64ですが
この記事の内容はmacOSには当てはまりません。
Linuxという表記を省略してAArch64と表記します。
(試してませんが、Apple SiliconのmacOSではこの記事に書いているひと手間がなくても簡単にSeleniumは動くはず)
LinuxはRocky Linux 9を使用しているため
パッケージマネージャのコマンドはdnfで記載しています。
他のディストリビューションの場合はパッケージマネージャを読み替える、
そのパッケージマネージャでインストールできない場合は手動でインストールするなどしてください。
TL;DR
AArch64でSeleniumを動かすためには
- Chromeは無いのでChromiumをインストールする
- Selenium Managerでのドライバ自動インストールはできないので、
手動でドライバをインストールしてパスを指定する
前提(Selenium Manager)
通常(今回対象としているAArch64のLinux以外においては)
詳細はこちらの記事がわかりやすいのですが
以前はSeleniumを使うためには手動でブラウザごとのドライバをインストールする必要がありましたが、現在はSelenium Managerという仕組みで自動でドライバのダウンロードまで行ってくれます。
nugetから以下をインストールして
ブラウザがインストールされていれば、ドライバは意識せず以下のコードのみでSeleniumが動きます。
using OpenQA.Selenium.Chrome;
ChromeDriverService service = ChromeDriverService.CreateDefaultService("");
ChromeOptions options = new();
options.AddArgument("--headless=new");
using ChromeDriver driver = new(service,options);
driver.Navigate().GoToUrl("https://domains.google.com/checkip");
driver.Quit();
ChromeDriverService service = ChromeDriverService.CreateDefaultService("");
のコードのところで自動的にブラウザに対応したドライバ(この例ではChromedriver)をダウンロードしてきてくれます。
AArch64では
では、上記前提に従ってSeleniumを動かそうとしてみると
Chromeは?
CUIで使っているLinuxには普通はブラウザ(Chrome)は入っていません。
インストールしようかと思って調べると分かるのですが、AArch64のChromeは配布されていません。
実行してみると
(Chromeをどうするのかは後でまとめて解説しますが)
ブラウザの問題が解決したとして、前述のソースコードを実行してみると
以下のようなエラーが出てしまいます。
Unhandled exception. OpenQA.Selenium.WebDriverException: Error starting process: /******/bin/Debug/net8.0/selenium-manager/linux/selenium-manager --browser "chrome" --language-binding csharp --output json
---> System.ComponentModel.Win32Exception (8): An error occurred trying to start process '/******/bin/Debug/net8.0/selenium-manager/linux/selenium-manager' with working directory '/******'. Exec format error
at System.Diagnostics.Process.ForkAndExecProcess(ProcessStartInfo startInfo, String resolvedFilename, String[] argv, String[] envp, String cwd, Boolean setCredentials, UInt32 userId, UInt32 groupId, UInt32[] groups, Int32& stdinFd, Int32& stdoutFd, Int32& stderrFd, Boolean usesTerminal, Boolean throwOnNoExec)
at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
at OpenQA.Selenium.SeleniumManager.RunCommand(String fileName, String arguments)
--- End of inner exception stack trace ---
at OpenQA.Selenium.SeleniumManager.RunCommand(String fileName, String arguments)
at OpenQA.Selenium.SeleniumManager.BinaryPaths(String arguments)
at OpenQA.Selenium.DriverFinder.BinaryPaths()
at OpenQA.Selenium.DriverFinder.GetDriverPath()
at OpenQA.Selenium.Chromium.ChromiumDriver.GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout)
at OpenQA.Selenium.Chromium.ChromiumDriver..ctor(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout)
at OpenQA.Selenium.Chrome.ChromeDriver..ctor(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout)
at OpenQA.Selenium.Chrome.ChromeDriver..ctor(ChromeDriverService service, ChromeOptions options)
at Program.<Main>$(String[] args) in /******/Program.cs:line 6
このエラーの内容ですが、selenium-manager/linux/selenium-manager
がExec format error
と言っています。
Selenium Managerが自動でドライバをダウンロードしてくると言いましたが
このSelenium Manager自体もバイナリであり、
ビルドした時点でまずはプログラムの出力先のselenium-managerというフォルダにダウンロードされます。
このフォルダにはWindows向け・macOS向け・Linux向けそれぞれのバイナリが入っているのですがLinux向けはx64のバイナリしか入っていません。
x64向けのバイナリなのでAArch64で実行されるとExec format error
になっているというわけです。
試しにこのバイナリを単独で実行してもエラーになることが確認できます。
$ bin/Debug/net8.0/selenium-manager/linux/selenium-manager
-bash: bin/Debug/net8.0/selenium-manager/linux/selenium-manager: cannot execute binary file: Exec format error
ちなみにSeleniumのサイトを見ると、現時点では今以上のアーキテクチャをサポートするつもりは無いようで、AArch64の場合の対応方法も記載されています。
https://www.selenium.dev/documentation/selenium_manager/
どうすれば動くのか?
ブラウザ(インストール)
ChromeはありませんがChromiumはあります。Chromiumはdnfからインストールできます。
$ sudo dnf install chromium
ドライバ(インストール&パス指定)
Selenium Managerで自動でドライバをインストールすることはできませんが
手動でインストールしてドライバのパスを指定することができます。
こちらもdnfでインストールできます。
$ sudo dnf install chromedriver
その上で、プログラム側ではSelenium Managerを使わせないように
インストール済みのドライバのパスを指定します。
パスを指定するとSelenium Managerでのドライバ自動インストールは動作しません。
using OpenQA.Selenium.Chrome;
ChromeDriverService service = ChromeDriverService.CreateDefaultService("/usr/bin/chromedriver");
ChromeOptions options = new();
options.AddArgument("--headless=new");
using ChromeDriver driver = new(service,options);
driver.Navigate().GoToUrl("https://domains.google.com/checkip");
driver.Quit();
3行目のChromeDriverService service = ChromeDriverService.CreateDefaultService("/usr/bin/chromedriver");
の部分です。
変更する部分はここだけで、他は前出のプログラムと同じです。
dnfでドライバインストールした場合、通常は上記のパスになると思いますが
不明な場合は以下のコマンドで確認してください。
$ which chromedriver
/usr/bin/chromedriver
ちなみにブラウザのパスもoptions.BinaryLocation
というプロパティで指定できますが、Chromiumの場合でも指定せずに動きました。