rust-headless-chromeでブラウザを操作してみる

この記事ではrust-headless-chromeの基本的な使い方を紹介します。

## rust-headless-chromeとは

https://github.com/rust-headless-chrome/rust-headless-chrome

rust-headless-chromeとはRust版の[Puppeteer](https://github.com/puppeteer/puppeteer)で、Chrome DevTools teamにメンテナンスされています。

Rust版とは言ったもののPuppeteerと100%同じ機能ではないそうです。
本記事執筆時点(2024/5/1)で、以下のことはできないと記載されていますので、その場合はPuppeteerの使用を検討してください。

> - Dealing with frames
> - Handling file picker / chooser interactions
> - Tapping touchscreens
> - Emulating different network conditions (DevTools can alter latency, throughput, offline status, 'connection type')
> - Viewing timing information about network requests
> - Reading the SSL certificate
> - Replaying XHRs
> - HTTP Basic Auth
> - Inspecting `EventSource`s (aka server-sent events or SSEs)
> - WebSocket inspection

:::note info
※Playwrightとの比較については公式ドキュメントには書かれていませんでしたが、最近ではPuppteerよりもPlaywrightの方が主流ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516474/634da2c0-568d-9526-1260-238bc6b3d514.png)
:::

## 準備

まずはrust-headless-chromeをインストールします。

```
cargo new rust-headless-chrome-playground
cd rust-headless-chrome-playground
cargo add headless_chrome
```

```rust:Cargo.toml
[package]
name = "rust-headless-chrome-playground"
version = "0.1.0"
edition = "2021"

[dependencies]
headless_chrome = "1.0.9"
```

## 使い方

### ブラウザを立ち上げる

```rust
let browser = Browser::default()?;
```

実際に操作されているところを見たい場合はヘッドレスモードをオフにして実行してください。

```rust
let browser = Browser::new(LaunchOptions {
    headless:false, // ←これをfalseにする
    ..Default::default() // ←他はデフォルトの値を設定する
})?;
```

### ページを開く

```rust
let tab = browser.new_tab()?;
tab.navigate_to("https://www.google.com/")?;
```

### 要素にフォーカスを当てる

```rust
tab.wait_for_element("textarea[title=検索]")?.focus()?;
```

入力の要素に対しては実際の動作と同様にクリックすることでフォーカスを当てることもできます。

```rust
tab.wait_for_element("textarea[title=検索]")?.click()?;
```

### エンターキーを押す

```rust
tab.type_str("Rust")?.press_key("Enter")?;
```

テキストエリアにRustと入力してエンターキーを押します。

### 要素を探す

```rust
tab.find_element("title")?
```

find_elementで要素が取得できます。

### 要素のテキストを取得する

```rust
tab.find_element("title")?.get_inner_text()?
```

```rust
tab.wait_for_element("title")?.get_inner_text()?
```

get_inner_textはElement構造体に定義されているため、find_element・wait_for_elementのどちらの場合でも使えます。

### スクリーンショットを撮る

```rust
let jpeg_data = tab.capture_screenshot(
        Page::CaptureScreenshotFormatOption::Jpeg,
        None,
        None,
        true)?;
fs::write("screenshot.jpg", jpeg_data)?;
```

ある要素だけのスクショを撮ることもできます.
ただし、その場合はcapture_screenshotの引数はフォーマットを指定のみになります。

```rust
let jpeg_data = tab.wait_for_element("div")?.capture_screenshot(
        Page::CaptureScreenshotFormatOption::Jpeg,
        )?;
```

## 検索した内容のスクショを取得する

サンプルコードの内容を参考にGoogleで「Rust」と検索して、検索結果のスクショを取得してみます。

```rust:main.rs
use std::error::Error;

use headless_chrome::{Browser, LaunchOptions};
use headless_chrome::protocol::cdp::Page;

use std::fs;

fn main() {
    browse().unwrap();
}

fn browse() -> Result<(), Box<dyn Error>> {
    // ブラウザを立ち上げる
    let browser = Browser::new(LaunchOptions {
        headless:false,
        ..Default::default()
    })?;

    // タブを開く
    let tab = browser.new_tab()?;

    // https://www.google.com/に遷移する
    tab.navigate_to("https://www.google.com/")?;

    // 検索エリアにフォーカスを当てる
    tab.wait_for_element("textarea[title=検索]")?.focus()?;

    // 検索エリアにRustと入力し、エンターキーを押す
    tab.type_str("Rust")?.press_key("Enter")?;
    tab.wait_until_navigated()?;
    assert_eq!(tab.find_element("title")?.get_inner_text()?, "Rust - Google 検索");

    // 遷移後スクリーンショットを保存する
    let jpeg_data = tab.capture_screenshot(
        Page::CaptureScreenshotFormatOption::Jpeg,
        None,
        None,
        true)?;
    fs::write("screenshot.jpg", jpeg_data)?;

    Ok(())
}
```


`cargo run`を実行するとブラウザが立ち上がり、
screenshot.jpgがrust-headless-chrome-playgroundディレクトリの直下に保存されているはずです。

![run.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516474/b39964b2-94dd-650b-0d43-d92797e02a8c.gif)