playwright update
This commit is contained in:
@@ -45,7 +45,7 @@ aes-gcm = "0.10"
|
|||||||
cbc = { version = "0.1", features = ["alloc"] }
|
cbc = { version = "0.1", features = ["alloc"] }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
chromiumoxide = { version = "0.7", features = ["tokio-runtime"] }
|
chromiumoxide = { version = "0.7", features = ["tokio-runtime"] }
|
||||||
playwright = "0.0.20"
|
playwright-rs = "0.7"
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
warnings = "warn"
|
warnings = "warn"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use playwright::Playwright;
|
use playwright_rs::{LaunchOptions, Page, Playwright};
|
||||||
use playwright::api::Page;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::{sleep, timeout};
|
use tokio::time::{sleep, timeout};
|
||||||
@@ -52,24 +51,29 @@ pub async fn wait_for_attribute(
|
|||||||
attribute: &str,
|
attribute: &str,
|
||||||
wait_timeout: Duration,
|
wait_timeout: Duration,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
// _pw must be kept alive — dropping it kills the driver process.
|
// _browser is kept alive (Arc inside Playwright) — the page borrows from it.
|
||||||
let (_pw, page) = open_page(url).await?;
|
// The Page holds its own server reference, so dropping our local Playwright
|
||||||
|
// handle does not shut the driver down.
|
||||||
|
let (page, _browser, _playwright) = open_page(url).await?;
|
||||||
|
|
||||||
let xpath_js = serde_json::to_string(xpath).unwrap_or_default();
|
let xpath_js = serde_json::to_string(xpath).unwrap_or_default();
|
||||||
let attr_js = serde_json::to_string(attribute).unwrap_or_default();
|
let attr_js = serde_json::to_string(attribute).unwrap_or_default();
|
||||||
|
// playwright-rs's evaluate_value takes a single JS expression, so wrap the
|
||||||
|
// multi-statement lookup in an IIFE. The return value is auto-converted to
|
||||||
|
// a String by the Playwright protocol layer.
|
||||||
let js = format!(
|
let js = format!(
|
||||||
r#"() => {{
|
r#"(() => {{
|
||||||
var r = document.evaluate({xpath_js}, document, null,
|
var r = document.evaluate({xpath_js}, document, null,
|
||||||
XPathResult.FIRST_ORDERED_NODE_TYPE, null);
|
XPathResult.FIRST_ORDERED_NODE_TYPE, null);
|
||||||
var el = r.singleNodeValue;
|
var el = r.singleNodeValue;
|
||||||
if (!el) return '';
|
if (!el) return '';
|
||||||
return el.getAttribute({attr_js}) || el[{attr_js}] || '';
|
return el.getAttribute({attr_js}) || el[{attr_js}] || '';
|
||||||
}}"#
|
}})()"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = timeout(wait_timeout, async {
|
let result = timeout(wait_timeout, async {
|
||||||
loop {
|
loop {
|
||||||
match page.evaluate::<(), String>(&js, ()).await {
|
match page.evaluate_value(&js).await {
|
||||||
Ok(s) if !s.is_empty() => return Some(s),
|
Ok(s) if !s.is_empty() => return Some(s),
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => eprintln!("[playwright] evaluate error: {e}"),
|
Err(e) => eprintln!("[playwright] evaluate error: {e}"),
|
||||||
@@ -86,43 +90,48 @@ pub async fn wait_for_attribute(
|
|||||||
|
|
||||||
// ── internals ─────────────────────────────────────────────────────────────────
|
// ── internals ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async fn open_page(url: &str) -> Option<(Playwright, Page)> {
|
/// Launches Playwright (Node server + bundled Chromium), opens a page on
|
||||||
let pw = Playwright::initialize()
|
/// `url`, and returns a handle. Caller is responsible for keeping the
|
||||||
|
/// returned `Playwright` and `Browser` handles alive while the page is in
|
||||||
|
/// use — dropping the browser closes the page and driver.
|
||||||
|
async fn open_page(
|
||||||
|
url: &str,
|
||||||
|
) -> Option<(Page, playwright_rs::Browser, Playwright)> {
|
||||||
|
let playwright = Playwright::launch()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| eprintln!("[playwright] init error: {e}"))
|
.map_err(|e| eprintln!("[playwright] init error: {e}"))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
let args: Vec<String> = CHROME_ARGS.iter().map(|s| s.to_string()).collect();
|
let args: Vec<String> = CHROME_ARGS.iter().map(|s| s.to_string()).collect();
|
||||||
|
|
||||||
let browser = pw
|
let options = LaunchOptions::new()
|
||||||
.chromium()
|
|
||||||
.launcher()
|
|
||||||
.executable(Path::new("/usr/bin/google-chrome"))
|
|
||||||
.headless(true)
|
.headless(true)
|
||||||
.args(&args)
|
.chromium_sandbox(false)
|
||||||
.launch()
|
.args(args)
|
||||||
|
.executable_path(
|
||||||
|
Path::new("/usr/bin/google-chrome")
|
||||||
|
.to_str()
|
||||||
|
.unwrap_or("/usr/bin/google-chrome")
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let browser = playwright
|
||||||
|
.chromium()
|
||||||
|
.launch_with_options(options)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| eprintln!("[playwright] launch error: {e}"))
|
.map_err(|e| eprintln!("[playwright] launch error: {e}"))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
let context = browser
|
let page = browser
|
||||||
.context_builder()
|
|
||||||
.build()
|
|
||||||
.await
|
|
||||||
.map_err(|e| eprintln!("[playwright] context error: {e}"))
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
let page = context
|
|
||||||
.new_page()
|
.new_page()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| eprintln!("[playwright] new_page error: {e}"))
|
.map_err(|e| eprintln!("[playwright] new_page error: {e}"))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
page.goto_builder(url)
|
page.goto(url, None)
|
||||||
.goto()
|
|
||||||
.await
|
.await
|
||||||
.map_err(|e| eprintln!("[playwright] goto error: {e}"))
|
.map_err(|e| eprintln!("[playwright] goto error: {e}"))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
Some((pw, page))
|
Some((page, browser, playwright))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user