on_request/on_response stored callbacks in append-only Vecs with no way to remove one, so a crawler that installed a response-body capture hook for one phase could never detach it: it fired for the shared client's whole lifetime, re-registering across navigations piled up duplicates, and (because the client is shared per context) it saw sibling pages' requests too. Tag each callback with a stable id. on_request/on_response now return the id and off_request/off_response (remove_on_request/remove_on_response on the client) detach a specific one. Storage is Vec<(u64, Callback)>; all fire sites destructure the tuple. Closes #408.
obscura
Embeddable Rust API for the Obscura
headless browser. Drive a real V8 + DOM browser (Browser, Page, Element,
CookieStore) directly from Rust, with no separate process or CDP round-trips.
Install
This crate is not published to crates.io, so depend on it via git. Building it
compiles Obscura from source, including its embedded V8 (deno_core), so the
first build is large and slow.
[dependencies]
obscura = { git = "https://github.com/h4ckf0r0day/obscura", features = ["api"] }
tokio = { version = "1", features = ["rt", "macros"] }
anyhow = "1"
Usage
use obscura::Browser;
use std::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let browser = Browser::builder()
.stealth(true)
.storage_dir("/tmp/cookies")
.build()?;
let mut page = browser.new_page().await?;
page.goto("https://example.com").await?;
let el = page.wait_for_selector("a", Duration::from_secs(5)).await?;
println!("{} -> {:?}", el.text(), el.attribute("href"));
Ok(())
}
See examples/basic.rs for a runnable version (cargo run --example basic).