Async browser automation crate for Rust with runtime agnosticism.
Surfer uses the WebDriver protocol, which requires:
-
A browser installed on your system:
- Firefox, or
- Chrome/Chromium
-
The corresponding WebDriver binary in your PATH:
geckodriverfor Firefoxchromedriverfor Chrome
Arch/Manjaro:
sudo pacman -S geckodriver # for Firefox
sudo pacman -S chromedriver # for ChromeUbuntu/Debian:
# geckodriver - download from https://github.com/mozilla/geckodriver/releases
# chromedriver - download from https://chromedriver.chromium.org/downloadsmacOS:
brew install geckodriver
brew install chromedriverAdd to your Cargo.toml:
[dependencies]
surfer-rs = "0.1"| Feature | Description | Default |
|---|---|---|
tokio-runtime |
Use tokio async runtime | Yes |
smol-runtime |
Use smol async runtime | No |
cli |
Build the CLI binary | Yes |
To use smol instead of tokio:
[dependencies]
surfer-rs = { version = "0.1", default-features = false, features = ["smol-runtime"] }# Start browser (launches geckodriver + Firefox)
surfer start
surfer start --browser chrome --headless
# Navigate
surfer goto example.com
surfer back
surfer forward
surfer refresh
# Discover elements
surfer inputs # list input/textarea/select elements
surfer buttons # list buttons
surfer links # list links
surfer find "selector" # find by CSS selector
surfer context "selector" # show DOM context
# Interact by index (from inputs/buttons output)
surfer input 0 "hello" # set input value
surfer click 0 # click button by index
surfer select 2 "option" # select dropdown option
surfer enter 0 # press Enter
# Interact by selector
surfer click "button.submit"
surfer type "#email" "user@example.com"
surfer fill email "user@example.com" # by name or id
surfer search "query" # find search box and submit
surfer submit # submit first form
# Utilities
surfer screenshot output.png
surfer js "document.title"
surfer html
surfer url
surfer title
# Stop browser
surfer stop
surfer statususe surfer_rs::{WebDriverSurfer, Surfer, BrowserConfig};
#[tokio::main]
async fn main() -> Result<(), surfer_rs::SurferError> {
// Connect to running WebDriver (start geckodriver first)
let config = BrowserConfig::default();
let browser = WebDriverSurfer::connect(&config).await?;
// Navigate
browser.goto("https://example.com").await?;
// Discover
let inputs = browser.inputs().await?;
for input in &inputs {
println!("{}", input);
}
// Interact
browser.set_input(0, "hello").await?;
browser.click_button(0).await?;
// Or by selector
browser.fill("email", "user@example.com").await?;
browser.click("button[type=submit]").await?;
Ok(())
}surfer (Rust)
↓
fantoccini (WebDriver client)
↓
geckodriver/chromedriver (WebDriver server)
↓
Firefox/Chrome (browser)
Browser state is stored at ~/.local/share/surfer/browser.json:
{
"pid": 12345,
"port": 4444,
"browser_type": "firefox",
"webdriver_url": "http://localhost:4444",
"started_at": "2024-01-24T10:30:00Z"
}MIT