Rust无头浏览器库headless_chrome的使用:自动化网页操作与爬虫开发利器
Rust无头浏览器库headless_chrome的使用:自动化网页操作与爬虫开发利器
一个通过DevTools协议控制无头Chrome或Chromium的高级API。它是Node库Puppeteer的Rust等效版本,由Chrome DevTools团队维护。
它并非100%与Puppeteer功能兼容,但足以满足大多数浏览器测试/网络爬虫用例,并提供了一些"高级"功能,例如:
- 网络请求拦截
- JavaScript覆盖率监控
- 打开隐身窗口
- 截取元素或整个页面的屏幕截图
- 将页面保存为PDF
- "有头"浏览
- 自动下载Linux/Mac/Windows的"已知良好"Chromium二进制文件
- 扩展预加载
快速开始
use std::error::Error;
use headless_chrome::Browser;
use headless_chrome::protocol::cdp::Page;
fn browse_wikipedia() -> Result<(), Box<dyn Error>> {
let browser = Browser::default()?;
let tab = browser.new_tab()?;
// 导航到维基百科
tab.navigate_to("https://www.wikipedia.org")?;
// 等待网络/JavaScript/DOM使搜索框可用
// 并点击它
tab.wait_for_element("input#searchInput")?.click()?;
// 输入查询并按`Enter`
tab.type_str("WebKit")?.press_key("Enter")?;
// 导航后我们应该在WebKit页面上
let elem = tab.wait_for_element("#firstHeading")?;
assert!(tab.get_url().ends_with("WebKit"));
/// 截取整个浏览器窗口的屏幕截图
let jpeg_data = tab.capture_screenshot(
Page::CaptureScreenshotFormatOption::Jpeg,
None,
None,
true)?;
// 将屏幕截图保存到磁盘
std::fs::write("screenshot.jpeg", jpeg_data)?;
/// 仅截取WebKit信息框的屏幕截图
let png_data = tab
.wait_for_element("#mw-content-text > div > table.infobox.vevent")?
.capture_screenshot(Page::CaptureScreenshotFormatOption::Png)?;
// 将屏幕截图保存到磁盘
std::fs::write("screenshot.png", png_data)?;
// 在页面中运行JavaScript
let remote_object = elem.call_js_fn(r#"
function getIdTwice () {
// `this`始终是调用`call_js_fn`的元素
const id = this.id;
return id + id;
}
"#, vec![], false)?;
match remote_object.value {
Some(returned_string) => {
dbg!(&returned_string);
assert_eq!(returned_string, "firstHeadingfirstHeading".to_string());
}
_ => unreachable!()
};
Ok(())
}
自动获取Chrome二进制文件
[dependencies]
headless_chrome = {git = "https://github.com/rust-headless-chrome/rust-headless-chrome", features = ["fetch"]}
有关更完整的示例,请查看tests/simple.rs
和examples
。
在运行示例之前。确保在您的Cargo项目的
Cargo.toml
依赖中添加failure crate
它不能做什么?
Chrome DevTools协议非常庞大。目前,Puppeteer支持的功能比我们多得多。一些缺失的功能包括:
- 处理框架
- 处理文件选择器/选择器交互
- 点击触摸屏
- 模拟不同的网络条件(DevTools可以更改延迟、吞吐量、离线状态、“连接类型”)
- 查看网络请求的计时信息
- 读取SSL证书
- 重放XHR
- HTTP基本认证
- 检查
EventSource
(又称服务器发送事件或SSE) - WebSocket检查
如果您有兴趣添加其中一项功能但需要一些关于如何开始的建议,请通过创建问题或发送电子邮件至alistair@sunburnt.country
与我联系。
相关crates
- fantoccini使用WebDriver,因此它可以与Chrome以外的浏览器一起使用。它也是异步的并且基于Tokio,不像
headless_chrome
那样具有同步API并且仅使用普通旧线程实现。Fantoccini也存在时间更长且经过更多实战测试。它不支持Chrome DevTools特定功能,如JS覆盖率。
测试
对于调试输出,在运行cargo test
之前设置这些环境变量:
RUST_BACKTRACE=1 RUST_LOG=headless_chrome=trace
版本号
从v0.2.0开始,我们尝试严格遵循SemVar。
故障排除
如果您遇到与超时相关的错误,您可能需要在内核中或作为setuid沙盒启用沙盒。Puppeteer有一些关于如何执行此操作的信息。
贡献
非常欢迎拉取请求和问题,即使只是经验报告。如果您发现任何令人沮丧或困惑的地方,请告诉我!
完整示例代码
use std::error::Error;
use headless_chrome::Browser;
use headless_chrome::protocol::cdp::Page;
fn main() -> Result<(), Box<dyn Error>> {
// 创建浏览器实例
let browser = Browser::default()?;
// 创建新标签页
let tab = browser.new_tab()?;
// 导航到目标网站
tab.navigate_to("https://www.example.com")?;
// 等待页面加载完成
tab.wait_until_navigated()?;
// 查找元素并交互
if let Ok(element) = tab.wait_for_element("input[type='search']") {
element.click()?;
element.type_str("Rust programming")?;
element.press_key("Enter")?;
}
// 等待搜索结果加载
tab.wait_for_element(".search-results")?;
// 截取屏幕截图
let screenshot_data = tab.capture_screenshot(
Page::CaptureScreenshotFormatOption::Png,
None,
None,
true
)?;
// 保存截图
std::fs::write("search_results.png", screenshot_data)?;
// 提取页面内容
let page_content = tab.get_content()?;
println!("页面内容长度: {} 字符", page_content.len());
// 执行JavaScript代码
let js_result = tab.evaluate("document.title", false)?;
if let Some(title) = js_result.value {
println!("页面标题: {}", title);
}
Ok(())
}
这个示例展示了headless_chrome库的基本用法,包括页面导航、元素交互、屏幕截图和JavaScript执行等核心功能。
完整示例demo
基于提供的示例代码,这里是一个更完整的headless_chrome使用示例:
use std::error::Error;
use headless_chrome::Browser;
use headless_chrome::protocol::cdp::Page;
fn main() -> Result<(), Box<dyn Error>> {
println!("开始headless_chrome示例...");
// 创建浏览器实例(默认无头模式)
let browser = Browser::default()?;
println!("浏览器实例创建成功");
// 创建新标签页
let tab = browser.new_tab()?;
println!("新标签页创建成功");
// 导航到目标网站
println!("导航到示例网站...");
tab.navigate_to("https://www.example.com")?;
// 等待页面加载完成
tab.wait_until_navigated()?;
println!("页面加载完成");
// 获取页面标题
let js_result = tab.evaluate("document.title", false)?;
if let Some(title) = js_result.value {
println!("页面标题: {}", title);
}
// 截取整个页面的屏幕截图
println!("正在截取屏幕截图...");
let screenshot_data = tab.capture_screenshot(
Page::CaptureScreenshotFormatOption::Png,
None, // 裁剪区域(None表示整个页面)
None, // 缩放比例(None表示原始大小)
true // 从视口截图
)?;
// 保存截图到文件
std::fs::write("example_screenshot.png", screenshot_data)?;
println!("屏幕截图已保存为 example_screenshot.png");
// 提取页面内容
let page_content = tab.get_content()?;
println!("页面内容长度: {} 字符", page_content.len());
// 示例:查找特定元素并交互
println!("尝试查找搜索框元素...");
if let Ok(search_input) = tab.wait_for_element("input[type='search']") {
println!("找到搜索框元素");
// 点击搜索框
search_input.click()?;
println!("点击搜索框");
// 输入搜索关键词
search_input.type_str("Rust headless browser")?;
println!("输入搜索关键词");
// 按Enter键搜索
search_input.press_key("Enter")?;
println!("按Enter键搜索");
// 等待搜索结果
tab.wait_for_element(".search-results")?;
println!("搜索结果加载完成");
// 截取搜索结果页面
let results_screenshot = tab.capture_screenshot(
Page::CaptureScreenshotFormatOption::Jpeg,
None,
None,
true
)?;
std::fs::write("search_results.jpeg", results_screenshot)?;
println!("搜索结果截图已保存");
} else {
println!("未找到搜索框元素,继续执行其他操作");
}
// 执行自定义JavaScript代码
println!("执行自定义JavaScript代码...");
let custom_js_result = tab.evaluate(r#"
// 获取页面中所有链接的数量
const linkCount = document.querySelectorAll('a').length;
// 返回页面信息对象
{
title: document.title,
linkCount: linkCount,
description: document.querySelector('meta[name="description"]')?.content || '无描述'
}
"#, false)?;
if let Some(result) = custom_js_result.value {
println!("JavaScript执行结果: {}", result);
}
// 获取当前URL
let current_url = tab.get_url();
println!("当前页面URL: {}", current_url);
// 导航到新页面示例
println!("导航到新页面...");
tab.navigate_to("https://httpbin.org/html")?;
tab.wait_until_navigated()?;
// 等待特定元素出现
if let Ok(paragraph) = tab.wait_for_element("p") {
let text_content = paragraph.get_description()?;
println!("找到段落元素: {:?}", text_content);
}
println!("headless_chrome示例执行完成!");
Ok(())
}
这个完整示例展示了headless_chrome库的核心功能,包括:
- 浏览器实例创建和标签页管理
- 页面导航和等待加载
- 元素查找和交互(点击、输入、按键)
- 屏幕截图功能
- JavaScript执行和结果获取
- 页面内容提取
- 错误处理和结果验证
要运行此示例,需要在Cargo.toml中添加依赖:
[dependencies]
headless_chrome = "0.9.0"
failure = "0.1.8"
这个示例可以作为开发网页自动化工具和网络爬虫的基础模板。
Rust无头浏览器库headless_chrome的使用指南
概述
headless_chrome是一个基于Chrome DevTools Protocol的Rust无头浏览器库,允许开发者以编程方式控制Chrome浏览器进行网页自动化操作和爬虫开发。
主要特性
- 完整的浏览器自动化支持
- 支持JavaScript执行和DOM操作
- 网络请求拦截和修改
- 截图和PDF生成功能
- Cookie和本地存储管理
安装方法
在Cargo.toml中添加依赖:
[dependencies]
headless_chrome = "0.9.0"
基本用法示例
1. 启动浏览器并访问网页
use headless_chrome::{Browser, LaunchOptionsBuilder};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建浏览器实例,设置为无头模式
let browser = Browser::new(
LaunchOptionsBuilder::default()
.headless(true)
.build()?
)?;
// 等待初始标签页
let tab = browser.wait_for_initial_tab()?;
// 导航到目标网页
tab.navigate_to("https://example.com")?;
// 等待页面完全加载
tab.wait_until_navigated()?;
Ok(())
}
2. 提取页面内容
// 获取页面标题
let title = tab.wait_for_element("h1")?
.get_inner_text()?;
println!("页面标题: {}", title);
// 获取所有链接
let links = tab.wait_for_elements("a")?;
for link in links {
let href = link.get_attribute_value("href")?;
println!("链接: {:?}", href);
}
3. 表单操作示例
// 填写用户名
tab.wait_for_element("input[name='username']")?
.click()?
.type_into("my_username")?;
// 填写密码
tab.wait_for_element("input[name='password']")?
.click()?
.type_into("my_password")?;
// 提交表单
tab.wait_for_element("button[type='submit']")?
.click()?;
4. 截图功能
// 截取整个页面
let screenshot_data = tab.capture_screenshot(
headless_chrome::protocol::page::ScreenshotFormat::PNG,
None,
true
)?;
// 保存截图到文件
std::fs::write("screenshot.png", screenshot_data)?;
5. 执行JavaScript
// 执行JavaScript代码获取页面标题
let result: String = tab.evaluate("document.title", true)?
.value
.expect("没有返回值")
.into_serde()?;
println!("通过JS获取的标题: {}", result);
高级功能
网络请求拦截
use headless_chrome::protocol::network::RequestPattern;
// 设置请求拦截模式
let patterns = vec![RequestPattern {
url_pattern: Some("*.jpg".to_string()),
resource_type: None,
interception_stage: Some("Request".to_string()),
}];
// 启用请求拦截
tab.enable_request_interception(patterns)?;
// 设置请求拦截回调
tab.set_request_interception(true, |event| {
if let Some(request) = event.request {
println!("拦截到请求: {}", request.url);
// 可以修改或阻止请求
}
Ok(())
})?;
处理弹窗和对话框
// 启用对话框处理
tab.enable_dialog_handling(|dialog| {
println!("对话框内容: {}", dialog.message);
dialog.accept("输入的内容")?; // 或者 dialog.dismiss()
Ok(())
})?;
最佳实践
- 错误处理:始终处理可能出现的错误,特别是网络超时和元素查找失败
- 等待策略:使用适当的等待方法避免竞态条件
- 资源管理:及时关闭不需要的标签页和浏览器实例
- 性能优化:禁用不必要的功能(如图片加载)以提高性能
常见问题解决
- 如果遇到连接问题,检查Chrome/Chromium是否正确安装
- 处理动态内容时使用适当的等待策略
- 对于复杂的SPA应用,考虑使用
wait_for_element_with_custom_timeout
这个库为Rust开发者提供了强大的网页自动化能力,特别适合需要高性能和可靠性的爬虫项目。
完整示例demo
use headless_chrome::{Browser, LaunchOptionsBuilder};
use headless_chrome::protocol::network::RequestPattern;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 启动无头浏览器
let browser = Browser::new(
LaunchOptionsBuilder::default()
.headless(true)
.build()?
)?;
let tab = browser.wait_for_initial_tab()?;
// 2. 访问网页
tab.navigate_to("https://httpbin.org/html")?;
tab.wait_until_navigated()?;
// 3. 提取页面内容
let title = tab.wait_for_element("h1")?
.get_inner_text()?;
println!("页面标题: {}", title);
// 4. 执行JavaScript
let js_result: String = tab.evaluate("document.title", true)?
.value
.expect("没有返回值")
.into_serde()?;
println!("JavaScript获取标题: {}", js_result);
// 5. 设置网络请求拦截(示例:拦截图片请求)
let patterns = vec![RequestPattern {
url_pattern: Some("*.png".to_string()),
resource_type: None,
interception_stage: Some("Request".to_string()),
}];
tab.enable_request_interception(patterns)?;
tab.set_request_interception(true, |event| {
if let Some(request) = event.request {
println!("拦截到请求: {}", request.url);
// 可以在这里修改或阻止请求
}
Ok(())
})?;
// 6. 截图功能
let screenshot_data = tab.capture_screenshot(
headless_chrome::protocol::page::ScreenshotFormat::PNG,
None,
true
)?;
std::fs::write("example_screenshot.png", screenshot_data)?;
println!("截图已保存为 example_screenshot.png");
// 7. 处理对话框(如果有)
tab.enable_dialog_handling(|dialog| {
println!("检测到对话框: {}", dialog.message);
dialog.dismiss()?;
Ok(())
})?;
// 等待一段时间以便观察
std::thread::sleep(Duration::from_secs(2));
println!("自动化操作完成!");
Ok(())
}
这个完整示例演示了headless_chrome库的主要功能,包括浏览器启动、页面导航、内容提取、JavaScript执行、网络请求拦截、截图和对话框处理。您可以根据实际需求修改和扩展这个示例。