Scriptone

Rust thirtyfour Webdriver Auto-Update and Execution

Rust’s thirtyfour is a tool similar to Selenium or Playwright that uses Webdriver to automate browser actions for testing web applications, performing scraping, or verifying values. To achieve automation with thirtyfour, the standard procedure involves installing the corresponding Webdriver in advance to match the browser version and then executing it. However, since Webdriver updates are required with every browser version upgrade, this process can be tedious. For programs handed over to non-technical users, issues with Webdriver updates often arise.

In other languages like Python with Selenium, an executable called SeleniumManager is bundled within Selenium. This allows for automatic Webdriver updates and provides the updated Webdriver path to Selenium for automatic startup. Among these, SeleniumManager, which handles Webdriver updates, is written in Rust and managed on GitHub, so it can be reused in Rust to enable automatic Webdriver updates for thirtyfour.

Below are the steps and sample code.

Code

The key point of this code is to reuse SeleniumManager to handle Webdriver updates effortlessly within Rust. The source code for SeleniumManager can be found on GitHub.

cargo.toml

In cargo.toml, specify selenium-manager to be fetched from GitHub, along with other necessary crates like thirtyfour and 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!("Failed to start ChromeDriver: {}", e))?;
        std::thread::sleep(Duration::from_secs(2)); // Wait for startup
        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
}

Explanation

After adding the dependencies in cargo.toml, you can reuse SeleniumManager by including use selenium_manager::get_manager_by_browser;. Passing the browser name to get_manager_by_browser automatically installs the corresponding Webdriver and returns the file path as a string. Then, executing start runs the appropriate version of Webdriver via command.

The subsequent steps follow the standard thirtyfour code writing process. In the sample code, it simply accesses example.com and outputs the page title.

Summary

Normally, Webdriver updates are required with browser version upgrades, but using SeleniumManager eliminates this step. While methods exist to run Selenium in a container using Docker, this approach doesn’t require Docker, making it easier to hand over to non-technical users for automation tasks like testing, scraping, or crawling. As it only involves reusing SeleniumManager, it’s a convenient method for developers as well, so please try it out.