Rust浏览器自动化库chromiumoxide_cdp的使用:通过Chrome DevTools协议实现高效网页操控与数据抓取

Rust浏览器自动化库chromiumoxide_cdp的使用:通过Chrome DevTools协议实现高效网页操控与数据抓取

chromiumoxide 提供了一个高级异步 API 来控制 Chrome 或 Chromium 通过 DevTools 协议。它支持所有类型的 Chrome DevTools 协议,并且可以启动一个 headless 或无头 Chrome/Chromium 实例,或者连接到已运行的实例。

使用方法

下面是使用 chromiumoxide 进行网页自动化操作的完整示例:

use futures::StreamExt;
use chromiumoxide::browser::{Browser, BrowserConfig};

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建一个带UI的浏览器实例(默认是无头模式)
    let (mut browser, mut handler) =
        Browser::launch(BrowserConfig::builder().with_head().build()?).await?;
    
    // 创建一个新任务来持续轮询处理程序
    let handle = async_std::task::spawn(async move {
        while let Some(h) = handler.next().await {
            if h.is_err() {
                break;
            }
        }
    });
    
    // 创建一个新页面并导航到URL
    let page = browser.new_page("https://en.wikipedia.org").await?;
    
    // 查找搜索框,输入搜索内容并回车
    page.find_element("input#searchInput")
        .await?
        .click()
        .await?
        .type_str("Rust programming language")
        .await?
        .press_key("Enter")
        .await?;

    // 等待导航完成并获取页面HTML内容
    let html = page.wait_for_navigation().await?.content().await?;
    
    browser.close().await?;
    handle.await;
    Ok(())
}

当前API还缺少一些功能,但Page::execute函数允许发送所有chromiumoxide_types::Command类型。大多数ElementPage函数基本上只是简化的命令构造和组合,比如Page::pdf:

pub async fn pdf(&self, params: PrintToPdfParams) -> Result<Vec<u8>> {
    let res = self.execute(params).await?;
    Ok(base64::decode(&res.data)?)
}

如果你需要其他功能,Page::execute函数允许你编写自己的命令包装器。

添加到项目

chromiumoxide支持async-stdtokio运行时。

默认使用async-std运行时:

chromiumoxide = { git = "https://github.com/mattsse/chromiumoxide", branch = "main"}

要使用tokio运行时,添加features = ["tokio-runtime"]并设置default-features = false:

chromiumoxide = { git = "https://github.com/mattsse/chromiumoxide", features = ["tokio-runtime"], default-features = false, branch = "main"}

自动下载Chromium

chromiumoxide默认会尝试在运行的计算机上查找已安装的Chromium版本。也可以使用fetcher自动下载和安装:

use std::path::Path;
use futures::StreamExt;
use chromiumoxide::browser::{BrowserConfig};
use chromiumoxide::fetcher::{BrowserFetcher, BrowserFetcherOptions};

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let download_path = Path::new("./download");
    async_std::fs::create_dir_all(&download_path).await?;
    let fetcher = BrowserFetcher::new(
        BrowserFetcherOptions::builder()
            .with_path(&download_path)
            .build()?,
    );
    let info = fetcher.fetch().await?;

    let config = BrowserConfig::builder()
        .chrome_executable(info.executable_path)
        .build()?,
    Ok(())
}

已知问题

  • 当手动关闭对实验性类型的支持时(export CDP_NO_EXPERIMENTAL=true),为PDL文件生成的rust文件无法编译。

故障排除

问题: 一个新的chromium实例被启动但随后超时。

解决方案: 检查你的chromium语言设置是否为英语。chromiumoxide尝试从chromium进程输出中解析调试端口,这仅限于英语。

许可证

MIT或Apache-2.0许可。

完整示例

这里是一个完整的网页抓取示例,展示了如何使用chromiumoxide抓取页面内容:

use futures::StreamExt;
use chromiumoxide::browser::{Browser, BrowserConfig};
use chromiumoxide::page::Page;

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 配置浏览器并启动
    let (browser, mut handler) = Browser::launch(
        BrowserConfig::builder()
            .with_head()  // 使用UI界面
            .build()?
    ).await?;

    // 处理浏览器事件
    let handle = async_std::task::spawn(async move {
        while let Some(h) = handler.next().await {
            if h.is_err() {
                break;
            }
        }
    });

    // 创建新页面并导航到目标URL
    let page = browser.new_page("https://example.com").await?;
    
    // 等待页面加载完成
    page.wait_for_navigation().await?;
    
    // 获取页面标题
    let title = page.title().await?;
    println!("页面标题: {}", title);
    
    // 获取页面HTML内容
    let content = page.content().await?;
    println!("页面内容长度: {} bytes", content.len());
    
    // 截图
    let screenshot = page.screenshot(None).await?;
    std::fs::write("screenshot.png", screenshot)?;
    
    // 关闭浏览器
    browser.close().await?;
    handle.await;
    
    Ok(())
}

这个示例展示了chromiumoxide的基本功能,包括:

  1. 启动浏览器
  2. 创建新页面
  3. 导航到URL
  4. 获取页面信息
  5. 截图
  6. 关闭浏览器

你可以根据需要扩展这个示例,添加更多的自动化操作,如填写表单、点击元素、处理JavaScript等。


1 回复

Rust浏览器自动化库chromiumoxide_cdp的使用指南

概述

chromiumoxide_cdp 是一个基于 Rust 的库,它通过 Chrome DevTools 协议 (CDP) 提供浏览器自动化功能。这个库允许开发者以编程方式控制 Chrome 或 Chromium 浏览器,实现网页操控、数据抓取等自动化任务。

主要特性

  • 完整的 Chrome DevTools 协议支持
  • 异步/等待友好的 API 设计
  • 支持无头(Headless)和有头模式
  • 网页导航、表单填写、点击等交互操作
  • JavaScript 执行与结果获取
  • 网络请求拦截与修改
  • 页面截图和 PDF 生成

安装

在 Cargo.toml 中添加依赖:

[dependencies]
chromiumoxide = "0.7"
tokio = { version = "1.0", features = ["full"] }

完整示例代码

下面是一个结合多个功能的完整示例,展示如何使用chromiumoxide_cdp进行网页自动化操作:

use chromiumoxide::browser::{Browser, BrowserConfig};
use chromiumoxide::cdp::browser_protocol::network::events::RequestWillBeSent;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 配置并启动浏览器
    let config = BrowserConfig::builder()
        .with_headless()  // 无头模式
        .build()?;
    
    let (browser, mut handler) = Browser::launch(config).await?;
    
    let handle = tokio::task::spawn(async move {
        while let Some(h) = handler.next().await {
            if h.is_err() {
                break;
            }
        }
    });
    
    // 2. 创建新页面并导航
    let page = browser.new_page("about:blank").await?;
    page.goto("https://www.example.com").await?;
    page.wait_for_navigation().await?;
    
    // 3. 获取页面信息
    let title = page.evaluate("document.title").await?;
    println!("页面标题: {:?}", title);
    
    // 4. 网络请求拦截示例
    page.enable_network().await?;
    let mut events = page.event_listener::<RequestWillBeSent>().await?;
    tokio::task::spawn(async move {
        while let Some(event) = events.next().await {
            println!("拦截到请求: {}", event.request.url);
        }
    });
    
    // 5. 执行复杂JavaScript
    let result = page
        .evaluate_with_args(
            "({a, b}) => { return a * b; }",
            json!({ "a": 6, "b": 7 })
        )
        .await?;
    println!("计算结果: {}", result.value::<i32>()?);
    
    // 6. 截图功能
    page.screenshot()
        .full_page(true)
        .await?
        .save("example_screenshot.png")
        .await?;
    
    // 7. 处理iframe示例(如果页面中有iframe)
    if let Ok(iframes) = page.get_iframes().await {
        if !iframes.is_empty() {
            println!("发现 {} 个iframe", iframes.len());
        }
    }
    
    // 关闭浏览器
    browser.close().await?;
    handle.await?;
    
    Ok(())
}

代码说明

  1. 首先配置并启动浏览器实例,使用无头模式
  2. 创建新页面并导航到示例网站
  3. 获取并打印页面标题
  4. 设置网络请求拦截,打印所有请求URL
  5. 执行带有参数的JavaScript并获取结果
  6. 截取整个页面并保存为PNG文件
  7. 检查页面中的iframe并打印数量

注意事项

  1. 运行此代码前请确保已安装Chrome或Chromium浏览器
  2. 网络请求拦截功能会捕获所有请求,可能会影响性能
  3. 截图功能需要页面完全加载后才能获得完整效果
  4. 实际使用时请添加适当的错误处理逻辑
回到顶部