---
title: "chromedpとは？GoでChromeを自動操作する使い方【v0.21対応・新API】"
url: "https://www.issoh.co.jp/tech/details/3489/"
published: 2024-09-04
updated: 2026-10-28
categories: ["Go"]
publisher: "株式会社一創"
---

# chromedpとは？GoでChromeを自動操作する使い方【v0.21対応・新API】

chromedpは、Chrome DevTools Protocol（CDP）でChromeやChromiumを操作するGoのライブラリです。外部のドライバは要らず、Goのコードだけでページ遷移・入力・クリック・スクリーンショット・PDF出力を書けます。2026年10月のv0.17.0からv0.21.0でAPIが全面的に書き換わり、旧Runやポインタ渡しの取得APIを使ったコードは、そのままではv0.21.0でビルドできません。この記事では、v0.21.0とGo 1.27.2で実際に動かしたコードを使って、新しい書き方・移行手順・つまずきやすいエラーを説明します。

## まとめ：chromedpの要点と最新版

- **最新版**：v0.21.0（2026年10月11日）。必要なGoは1.25以上。直接依存はcdprotoだけで、Go 1.25・1.26ではJSON実装（go-json-experiment）が間接依存に入る（`GOEXPERIMENT=jsonv2` 指定時を除く）。
- **書き方が変わった**：値を返さない操作は `chromedp.Do`、値を返す操作は `chromedp.Run` の戻り値で受け取る。`&title` のようなポインタ渡しは廃止。
- **セレクタは型で指定**：`chromedp.ByQuery` は無くなり、`chromedp.CSS("#id")` のように型で検索方法を選ぶ。
- **既定はヘッドレス**：画面を出すときは `WithVisibleWindow()`。起動したブラウザとは既定でパイプ接続する。
- **旧コードを動かすだけなら**：`go get github.com/chromedp/chromedp@v0.16.0` で旧API最後の版に固定できる。新規の開発は新APIで書く。

## chromedpとは：CDPでChromeを動かすGoライブラリ

chromedpは、Goの関数呼び出しを[Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)のコマンド（`Page.navigate` など）に変換してブラウザへ送ります。SeleniumのようにWebDriverを挟まないので、必要な外部プログラムはChrome本体だけです。[公式リポジトリ](https://github.com/chromedp/chromedp)はMITライセンスで、スター数は1万3千を超えています（2026年10月時点）。

CDPを使うツールは他にもあり、AIエージェントにブラウザを操作させる[Chrome DevTools MCPの導入手順と設定](/tech/details/17749/)も同じプロトコルの上で動きます。ブラウザ自動化の方式全体（WebDriver・CDP・拡張機能・画像認識）を比べたい場合は[Browser Automationの4つの方式と選び方](/tech/details/9129/)が参考になります。chromedpは、このうちCDP方式をGoから使う実装です。

### v0.17.0からの接続方式の変化

v0.17.0以降、chromedpが起動したブラウザとは `--remote-debugging-pipe` のパイプで通信します。既定ではデバッグ用のポートを開きません。WebSocketは起動済みブラウザへの接続に加え、`remote.WebSocket` を指定してchromedpからブラウザを起動する場合にも使えます。WebSocketを扱う処理は `github.com/chromedp/chromedp/remote` パッケージに分かれました。普段の使い方なら `remote` をインポートする必要はありません。

## chromedpのバージョンと必要なGo

2026年10月3日から11日までの9日間に、v0.17.0からv0.21.0まで8本のタグが出ており、版によって必要なGoとAPIが違います。`go.mod` の `go` 行を[Goモジュールプロキシ](https://proxy.golang.org/github.com/chromedp/chromedp/@latest)から版ごとに確認した結果です。

| 版       | 公開日        | 必要なGo | API | 主な変更                   |
| ------- | ---------- | ----- | --- | ---------------------- |
| v0.15.1 | 2026-04-01 | 1.26  | 旧   | ―                      |
| v0.16.0 | 2026-07-14 | 1.26  | 旧   | 旧API最後の版               |
| v0.17.0 | 2026-10-03 | 1.27  | 新   | ジェネリクス化・パイプ接続          |
| v0.18.0 | 2026-10-03 | 1.27  | 新   | remoteを別モジュール化         |
| v0.19.0 | 2026-10-04 | 1.27  | 新   | PrintToPDF・ExposeFunc等 |
| v0.20.0 | 2026-10-05 | 1.25  | 新   | Go要件を1.25へ緩和           |
| v0.21.0 | 2026-10-11 | 1.25  | 新   | remoteをコアへ統合           |

v0.17.0〜v0.19.xのビルドにはGo 1.27以上が必要です。手元がGo 1.25・1.26でも、既定の `GOTOOLCHAIN=auto` では必要なツールチェーンへ自動的に切り替わる場合があります。最新のv0.21.0はGo 1.25以上で動くので、手元のGoを上げずに使えます。Go本体の版の確認方法は[Go 1.27の新機能と最新バージョンの確認方法](/tech/details/10416/)にまとめています。

## chromedpのインストールと最初のプログラム

モジュールを初期化したディレクトリで次を実行します。

```
go mod init example.com/cdp-demo
go get github.com/chromedp/chromedp@v0.21.0
```

ページを開いてタイトルを読む最小のプログラムです。遷移のように値を返さない操作は `Do`、タイトルのように値を返す操作は `Run` に渡し、戻り値で受け取ります。

```
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/chromedp/chromedp"
)

func main() {
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    if err := chromedp.Do(ctx, chromedp.Navigate("https://example.com")); err != nil {
        log.Fatal(err)
    }
    title, err := chromedp.Run(ctx, chromedp.Title())
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(title)
}
```

`Run` が受け取れる操作は1つだけです。複数の操作を順に実行するなら `Do(ctx, a, b, c)` に並べ、値は最後に `Run` で読みます。

### Chrome本体の探し方とExecPath

chromedpは決まった順でChromeを探します。macOSは `/Applications/Chromium.app`、次に `/Applications/Google Chrome.app`。Linuxは `headless_shell`・`chromium`・`google-chrome` などをPATHから探します。WindowsではChromeが無ければMicrosoft Edgeを使います（v0.19.0から）。見つからないと `exec: "google-chrome": executable file not found in $PATH` で止まるので、そのときは `ExecPath` で場所を渡します。

```
opts := append(chromedp.DefaultExecAllocatorOptions[:],
    chromedp.ExecPath("/usr/bin/chrome-headless-shell"),
    chromedp.WindowSize(1280, 800),
)
allocCtx, cancelA := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancelA()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
```

## chromedpの基本操作：待機・取得・入力・JavaScript

よく使う操作の、v0.21.0での形です。

| 操作         | v0.21.0の書き方               | 戻り値       |
| ---------- | ------------------------- | --------- |
| 遷移         | Navigate(url)             | なし（Doで実行） |
| 表示を待つ      | WaitVisible(CSS("#a"))    | なし        |
| クリック       | Click(CSS("#go"))         | なし        |
| 入力         | SendKeys(CSS("#q"), "文字") | なし        |
| テキスト取得     | Text(CSS("#a"))           | string    |
| JavaScript | Evaluate\[int\]("式")      | 指定した型     |
| スクリーンショット  | FullScreenshot(100)       | \[\]byte  |

### 型付きセレクタ（CSS・ID・JSPath）

セレクタの型で検索方法が決まります。`CSS` は `querySelector` の最初の一致、`CSSAll` は全件、`ID` はid指定、`JSPath` はJavaScriptの式です。ただの文字列は旧APIの既定と同じ全文検索として扱われ、CSS・XPath・テキストのどれでも当たります。意図しない要素に当たるのを避けるなら、CSSセレクタは `CSS(...)` と明示するのが安全です。

### フォーム入力とクリック

入力欄に文字を入れて送信し、結果の要素が出るまで待つ例です。手元のテストページで、送信後に `q=chromedp` が表示されることを確認しました。

```
err := chromedp.Do(ctx,
    chromedp.Navigate(url),
    chromedp.SendKeys(chromedp.CSS("#q"), "chromedp"),
    chromedp.Click(chromedp.CSS("#go")),
    chromedp.WaitVisible(chromedp.CSS("#r")),
)
if err != nil {
    log.Fatal(err)
}
result, err := chromedp.Run(ctx, chromedp.Text(chromedp.CSS("#r")))
```

v0.18.0から、Ctrl・Alt・Metaを付けたキー入力では文字が入力されなくなりました。ショートカットを送るコードで文字入力も期待していた場合は、修飾キーを外して送り直します。

### JavaScriptの実行とnullの扱い

`Evaluate` は型引数で戻り値の型を指定します。v0.19.0から、JavaScriptの `null` を `string` や `int` で受けると `ErrJSNull` が返ります。以前はゼロ値がエラー無しで返っていたので、nullを許すなら `*string` のようなポインタ型で受けます。

```
n, err := chromedp.Run(ctx, chromedp.Evaluate[int]("document.querySelectorAll('h1').length"))

// null を string で受けると ErrJSNull
_, err = chromedp.Run(ctx, chromedp.Evaluate[string]("null"))
fmt.Println(errors.Is(err, chromedp.ErrJSNull)) // true
```

Promiseの完了を待つ場合は `Evaluate` に `chromedp.EvalAwaitPromise` を渡します。

## chromedpによる動的ページ（SPA）の取得と待機条件

chromedpの要素操作には自動待機があります。`Text` は対象要素が準備できるまで、`Click` と `SendKeys` は対象要素が表示されるまで待ちます。800ミリ秒後に要素を描画するテストページで、`WaitVisible` を挟まずに10秒の制限時間で `Text` を呼ぶと、約0.9秒後に描画後のテキストを取得できました。

```
ctx, cancel = context.WithTimeout(ctx, 10*time.Second)
defer cancel()

if err := chromedp.Do(ctx, chromedp.Navigate(url)); err != nil { // url は検証ページのURL
    log.Fatal(err)
}
// #app .item が描画されるまで Text が待つ
text, err := chromedp.Run(ctx, chromedp.Text(chromedp.CSS("#app .item")))
```

同じページで制限時間を300ミリ秒にすると、要素が現れる前に `context deadline exceeded` で失敗しました。失敗の原因は待機の有無ではなく制限時間です。待つのは要素の存在と表示までなので、「一覧の件数が増え終わった」「値が更新された」といったアプリ固有の完了は、`Poll` で条件式を指定するか、`WaitVisible` で完了後にだけ出る要素を待ちます。

静的なHTMLならブラウザを使わずHTTPで取得するほうが軽く、GoのスクレイピングフレームワークCollyの設定は[Collyの運用設定｜robots.txt・レート制限・文字化けを実測で解説](/tech/details/3320/)で扱っています。収集してよいか、利用規約やrobots.txtをどう扱うかは[スクレイピングとクローリング・APIの違いと法務の判断](/tech/details/13414/)を確認してください。

## chromedpのスクリーンショット・PDF出力と形式指定

ページ全体は `FullScreenshot`、表示範囲だけなら `CaptureScreenshot`、要素だけなら `Screenshot(CSS(...))` を使います。`FullScreenshot` の引数は画質で、ソースでは100のときPNG、それ以外はJPEGになります。出力の先頭バイトを確かめたところ、100はPNG（89 50 4E 47）、90はJPEG（FF D8 FF）でした。

```
png, err := chromedp.Run(ctx, chromedp.FullScreenshot(100))
if err != nil {
    log.Fatal(err)
}
if err := os.WriteFile("page.png", png, 0o644); err != nil {
    log.Fatal(err)
}

pdf, err := chromedp.Run(ctx, chromedp.PrintToPDF(
    chromedp.PDFPaper(chromedp.PaperA4),
    chromedp.PDFPrintBackground(),
))
if err != nil {
    log.Fatal(err)
}
if err := os.WriteFile("page.pdf", pdf, 0o644); err != nil {
    log.Fatal(err)
}
```

`PrintToPDF` はv0.19.0で追加された操作で、用紙・余白・倍率・ページ範囲・ヘッダーとフッターをオプションで指定できます。`PDFPrintBackground` は関数なので `()` が必要です。付け忘れると型エラーになります。同じ版で、ページからGoの関数を呼べる `ExposeFunc`、コンソール出力と未捕捉の例外をまとめて読む `Console`、ドラッグ操作の `DragAndDrop`、タップ操作の `Tap` も入りました（[v0.19.0のリリースノート](https://github.com/chromedp/chromedp/releases/tag/v0.19.0)）。

## chromedpのヘッドレス・画面表示・headless-shell

chromedpは既定でヘッドレス起動します。`DefaultExecAllocatorOptions` に `Headless` が入っているので、ヘッドレスにするための指定は要りません。デバッグで画面を見たいときは、v0.17.0から追加された `WithVisibleWindow()` を使います。

```
ctx, cancel := chromedp.NewContext(context.Background(), chromedp.WithVisibleWindow())
defer cancel()
```

旧来の `Flag("headless", false)` では `--hide-scrollbars` や `--mute-audio` が残っていました。`WithVisibleWindow` はこれらを外し、ウィンドウを最大化して開きます。コードを変えずに切り替えるなら、環境変数 `CHROMEDP_VISIBLEWINDOW` でも同じ動きになります。Linuxで画面（ディスプレイ）が無い環境では、ウィンドウを開けない旨のエラーが返ります。

サーバーやCIでは、Chrome一式の代わりにヘッドレス専用の `chrome-headless-shell` を使うと軽く済みます。chromedpチームは[Docker Hubのchromedp/headless-shell](https://hub.docker.com/r/chromedp/headless-shell)でこれを配布しており、`stable`・`beta`・`dev` のタグと版番号のタグがあります（2026年10月27日に更新）。Linuxの探索順は `headless_shell` から始まるので、このイメージの中で動かすと `ExecPath` を書かずに見つかります。

## 旧APIからv0.21.0へ移行する手順

旧版のこの記事で掲載していた、旧Runやポインタ渡しの取得APIを使うコードは、そのままではv0.21.0でビルドできません。旧版のコードを実際にビルドすると、次の5つのエラーが出ました。

```
too many arguments in call to chromedp.Run
undefined: chromedp.ByQuery
cannot use &text (value of type *string) as chromedp.QueryOption value in argument to chromedp.Text
too many arguments in call to chromedp.Title
too many arguments in call to chromedp.FullScreenshot
```

主な書き換えは次のとおりです。すべての変更は公式の[MIGRATION.md](https://github.com/chromedp/chromedp/blob/main/docs/MIGRATION.md)に11の段階に分けて載っており、上から順に当てるよう指示されています。

| 旧（v0.16.0まで）          | 移行後（v0.21.0）                  |
| --------------------- | ----------------------------- |
| Run(ctx, a, b, c)     | Do(ctx, a, b, c)              |
| Text(sel, &s)         | s, err := Run(ctx, Text(sel)) |
| Title(&t)             | t, err := Run(ctx, Title())   |
| Evaluate(js, &v)      | Run(ctx, Evaluate\[T\](js))   |
| Click("#a", ByQuery)  | Click(CSS("#a"))              |
| Click("a", ByID)      | Click(ID("a"))                |
| Tasks{…}              | Steps(…)                      |
| ActionFunc            | Func(…)                       |
| ListenTarget(ctx, fn) | Events(ctx, イベント)             |
| NewRemoteAllocator    | remote.NewAllocator           |

移行の判断は、コードの量で分けるのが現実的です。数百行の既存ツールで当面の機能追加が無いなら、`go get github.com/chromedp/chromedp@v0.16.0` で旧API最後の版に固定すれば、旧コードのままビルドできます（手元で確認済み。ただしGo 1.26以上が必要）。v0.19.0で直った不具合（ブラウザが落ちたときに `Call` が戻らない問題など）を取り込みたい場合や、新しく書くコードは新APIにします。古い操作をすぐ書き直せないときは、`Legacy(旧アクション)` で包むと新APIの `Do` に渡せます。

v0.18.0〜v0.20.1で `remote` を別モジュールとして `go.mod` に入れていた場合、v0.21.0へ上げると `ambiguous import` になります。`go mod tidy` でその行を消してから `go get github.com/chromedp/chromedp@v0.21.0` を実行します。また、v0.18.0以降はブラウザが異常終了したときのエラーが `context.Canceled` を包む形になったので、`err == context.Canceled` の比較は `errors.Is` に置き換えます。

## chromedpと他のブラウザ自動化ツールの比較

| ツール        | 言語    | 対応ブラウザ                  | 接続                 |
| ---------- | ----- | ----------------------- | ------------------ |
| chromedp   | Go    | Chrome/Chromium/Edge    | CDP（パイプ）           |
| go-rod     | Go    | Chrome/Chromium         | CDP                |
| Puppeteer  | JS/TS | Chrome/Firefox          | CDP・WebDriver BiDi |
| Playwright | 多言語   | Chromium/Firefox/WebKit | 独自プロトコル            |
| Selenium   | 多言語   | 主要ブラウザ                  | WebDriver          |

Goで書くなら候補はchromedpとgo-rodです。go-rodは最後のリリースが2024年7月のv0.116.2で（[モジュールプロキシの記録](https://proxy.golang.org/github.com/go-rod/rod/@latest)）、2年以上タグが出ていません。chromedpは2026年10月にcdproto v0.157.8へ上げており、新しいプロトコル定義を使いたいならchromedpです。一方、chromedpは10月に互換性の無い変更を短期間に重ねたので、版を固定せずに `go get -u` を回すCIは止まりやすくなっています。`go.mod` で版を固定し、上げるときに移行ガイドを読む運用にしてください。

FirefoxやSafari（WebKit）でも試験したい場合、chromedpは使えません。複数ブラウザのE2Eテストには[Playwright CLIのコマンド一覧](/tech/details/17730/)、既存のWebDriver資産があるなら[Seleniumの仕組みと使い方](/tech/details/4241/)が向きます。chromedpが向くのは、GoのバックエンドやCLIに、Chromeでの画面取得・PDF化・管理画面の自動操作を組み込む場合です。外部システムの操作を業務システムに組み込む開発は、[フルスクラッチ開発](/service/system/fullscratch/)でもご相談いただけます。

## chromedpのよくあるエラーと対処

### context deadline exceededの原因とタイムアウト設定

待っている要素が制限時間内に現れなかったときに出ます。`context.WithTimeout` で上限を決め、時間を延ばしても直らなければ、セレクタが実際のDOMと合っていないことを疑います。タイムアウトかどうかは `errors.Is(err, context.DeadlineExceeded)` で判定できます。

```
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
defer cancel()
```

### page load errorの判定と読み込み失敗の確認

ページが読み込めないと、v0.18.0からは `*LoadError` が返ります。到達できないポートを開いた試験では `page load error net::ERR_UNSAFE_PORT` となり、`errors.Is(err, chromedp.ErrPageLoad)` が真でした。エラー文字列を検索するより、この判定を使うほうが確実です。

### chrome failed to startの原因と起動ログの確認

v0.17.0以降、起動に失敗したときのエラーは `chrome failed to start:` で始まり、Chromeの出力が続きます。Dockerで共有ライブラリが足りないときなどに出るので、続く出力から足りないものを確認します。Chrome自体が見つからない場合は、前述の `executable file not found` になります。

### 旧APIコードのビルドエラーと移行・バージョン固定

`too many arguments in call to chromedp.Run` や `undefined: chromedp.ByQuery` は、v0.17.0以降に旧来の書き方のコードをビルドしたときのエラーです。上の移行表に従って書き換えるか、v0.16.0に固定します。

## よくある質問

### chromedpの最新バージョンはいくつですか？

2026年10月28日時点ではv0.21.0（2026年10月11日）です。Go 1.25以上で動きます。最新は[GitHubのリリース一覧](https://github.com/chromedp/chromedp/releases)で確認してください。

### chromedpはデフォルトでヘッドレスですか？

はい。画面を出すときはv0.17.0以降なら `WithVisibleWindow()` を指定します。

### chromedp/headless-shellとは何ですか？

chromedpチームがDocker Hubで配布している、ヘッドレス専用Chromeのイメージです。サーバーやCIでChrome一式を入れずにchromedpを動かすために使います。

### chromedpとPuppeteerの違いは何ですか？

どちらもCDPでブラウザを操作しますが、chromedpはGo、PuppeteerはJavaScript/TypeScriptのライブラリです。PuppeteerはFirefoxにも対応しますが、chromedpの対象はChromeとChromium系（WindowsではEdgeも）です。

### chromedpはPythonから使えますか？

使えません。chromedpはGo専用です。PythonでCDPを使うならPlaywrightのPython版などを選びます。

## 関連記事

- [Browser Automationとは？ブラウザ操作を自動化する4つの方式と選び方【2026年版】](/tech/details/9129/)
- [Chrome DevTools MCPの導入手順と設定｜58ツールの絞り込みと接続方式を実測](/tech/details/17749/)
- [Collyの運用設定｜robots.txt・レート制限・文字化けを実測で解説](/tech/details/3320/)
- [Go 1.27の新機能・変更点まとめ｜Goの最新バージョンは1.27、確認・更新方法と移行](/tech/details/10416/)
- [スクレイピングとは？クローリング・APIとの違いと実装・法務の判断を解説](/tech/details/13414/)

---

出典: [chromedpとは？GoでChromeを自動操作する使い方【v0.21対応・新API】](<https://www.issoh.co.jp/tech/details/3489/>)（株式会社一創）
