Rust无头浏览器库headless_chrome的使用:自动化网页操作与爬虫开发利器

Rust无头浏览器库headless_chrome的使用:自动化网页操作与爬虫开发利器

Build Status Crate API Discord channel

一个通过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.rsexamples

在运行示例之前。确保在您的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库的核心功能,包括:

  1. 浏览器实例创建和标签页管理
  2. 页面导航和等待加载
  3. 元素查找和交互(点击、输入、按键)
  4. 屏幕截图功能
  5. JavaScript执行和结果获取
  6. 页面内容提取
  7. 错误处理和结果验证

要运行此示例,需要在Cargo.toml中添加依赖:

[dependencies]
headless_chrome = "0.9.0"
failure = "0.1.8"

这个示例可以作为开发网页自动化工具和网络爬虫的基础模板。


1 回复

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(())
})?;

最佳实践

  1. 错误处理:始终处理可能出现的错误,特别是网络超时和元素查找失败
  2. 等待策略:使用适当的等待方法避免竞态条件
  3. 资源管理:及时关闭不需要的标签页和浏览器实例
  4. 性能优化:禁用不必要的功能(如图片加载)以提高性能

常见问题解决

  • 如果遇到连接问题,检查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执行、网络请求拦截、截图和对话框处理。您可以根据实际需求修改和扩展这个示例。

回到顶部