Scriptone

【Rust】thirtyfourのWebdriverの自動更新および実行

RustのthirtyfourはSeleniumやPlaywrightのようにWebdriverを使ってブラウザを自動で動かして、webアプリのテストをしたり、スクレイピングなどをして値の検証をしたりなどできるツールです。thirtyfourによる自動化を実現するのにあたって、通常の手順としては対応するブラウザのバージョンに合わせて事前にWebdriverをインストールして、それを実行する手順があります。ブラウザのバージョンアップの都度Webdriverのアップデートも必要なため、地味に煩雑な手順であり非技術者の方に渡すプログラムの場合にはWebdriverの更新周りのトラブルもしばしば起こります。

Pythonなど他言語のSeleniumではSelenium内にSeleniumManagerと呼ばれる実行ファイルが同梱されており、これを使ってWebdriverの自動更新を行い、更新後のWebdriverのpathをSeleniumに提供して自動でWebdriverを起動する仕組みが備わっています。このうちWebdriverの自動更新を担うSeleniumManagerがRust言語で記述されており、GitHub内で管理されているためRust言語のままSeleniumManagerを再利用し、thirtyfour向けのWebdriverの自動更新が可能です。

その手順とサンプルコードを記します。

コード

このコードで重要な点はSeleniumManagerを再利用して手間をかけずにWebDriverの更新をRust言語のまま実現することです。SeleniumManagerのソースコードはGitHubで確認できます。

cargo.toml

cargo.tomlにはselenium-managerをGitHubから読み込むように記述します。そのほかthirtyfourやtokioなど必要なクレートを導入します。

[package]
name = ""
version = "0.1.0"
edition = "2021"

[dependencies]
selenium-manager = { git = "<https://github.com/SeleniumHQ/selenium>", branch = "trunk" }
thirtyfour = "0.31.0"
tokio = "1.38.0"

main.rs

use selenium_manager::get_manager_by_browser;
use std::error::Error;
use std::process::Command;
use std::time::Duration;
use thirtyfour::prelude::*;

struct ChromeDriverManager {
    driver_path: String,
}

impl ChromeDriverManager {
    async fn new(browser: String) -> Result<Self, Box<dyn Error + Send + Sync>> {
        let driver_path = tokio::task::spawn_blocking(move || {
            let mut manager = get_manager_by_browser(browser)?;
            Ok::<_, Box<dyn Error + Send + Sync>>(manager.setup()?.to_str().ok_or("Invalid path")?.to_string())
        })
            .await??;

        Ok(Self { driver_path })
    }

    fn start(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
        Command::new(&self.driver_path)
            .spawn()
            .map_err(|e| format!("ChromeDriverの起動に失敗しました: {}", e))?;
        std::thread::sleep(Duration::from_secs(2)); // 起動待機
        Ok(())
    }
}

async fn run_browser_session() -> Result<(), Box<dyn Error + Send + Sync>> {
    let driver = WebDriver::new("<http://localhost:9515>", DesiredCapabilities::chrome()).await?;
    driver.goto("<https://www.example.com").await>?;
    println!("Page title: {}", driver.title().await?);
    driver.quit().await?;
    Ok(())
}

# [tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
    let chromedriver_manager = ChromeDriverManager::new("chrome".to_string()).await?;
    chromedriver_manager.start()?;
    run_browser_session().await
}

解説

cargo.tomlにdependenciesを追加した後で、use selenium_manager::get_manager_by_browser;を記載することでSeleniumManagerを再利用できます。get_manager_by_browserにブラウザ名を入力すると対応したブラウザのWebdriverを自動でインストールし、そのファイルパスの文字列を取得できます。その後、startを実行することでコマンド経由で対応するバージョンのWebdriverを自動で実行できます。

その後の手順は通常のthirtyfourのコード記述と同様となります。サンプルのコードとしてexample.comにアクセスし、そのページタイトルを出力するのみの処理となっております。

まとめ

ブラウザのバージョンアップにともにWebdriverの更新も通常は必要ですがSeleniumManagerの利用によりこの手順を省くことができます。Dockerを使いコンテナ上でSeleniumを動かす方法もありますが、この方法であればDockerなども不要で非技術者の方により渡しやすい方法でのテストやスクレイピング・クローリングなどの自動化を実現できます。SeleniumManagerを再利用するのみで開発者にとっても手軽な方法ですのでぜひお試しください。