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/[email protected]で旧API最後の版に固定できる。新規の開発は新APIで書く。
chromedpとは:CDPでChromeを動かすGoライブラリ
chromedpは、Goの関数呼び出しをChrome DevTools Protocolのコマンド(Page.navigate など)に変換してブラウザへ送ります。SeleniumのようにWebDriverを挟まないので、必要な外部プログラムはChrome本体だけです。公式リポジトリはMITライセンスで、スター数は1万3千を超えています(2026年10月時点)。
CDPを使うツールは他にもあり、AIエージェントにブラウザを操作させるChrome DevTools MCPの導入手順と設定も同じプロトコルの上で動きます。ブラウザ自動化の方式全体(WebDriver・CDP・拡張機能・画像認識)を比べたい場合はBrowser Automationの4つの方式と選び方が参考になります。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モジュールプロキシから版ごとに確認した結果です。
| 版 | 公開日 | 必要な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の新機能と最新バージョンの確認方法にまとめています。
chromedpのインストールと最初のプログラム
モジュールを初期化したディレクトリで次を実行します。
go mod init example.com/cdp-demo
go get github.com/chromedp/[email protected]
ページを開いてタイトルを読む最小のプログラムです。遷移のように値を返さない操作は 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・レート制限・文字化けを実測で解説で扱っています。収集してよいか、利用規約やrobots.txtをどう扱うかはスクレイピングとクローリング・APIの違いと法務の判断を確認してください。
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のリリースノート)。
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でこれを配布しており、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に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/[email protected] で旧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/[email protected] を実行します。また、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で(モジュールプロキシの記録)、2年以上タグが出ていません。chromedpは2026年10月にcdproto v0.157.8へ上げており、新しいプロトコル定義を使いたいならchromedpです。一方、chromedpは10月に互換性の無い変更を短期間に重ねたので、版を固定せずに go get -u を回すCIは止まりやすくなっています。go.mod で版を固定し、上げるときに移行ガイドを読む運用にしてください。
FirefoxやSafari(WebKit)でも試験したい場合、chromedpは使えません。複数ブラウザのE2EテストにはPlaywright CLIのコマンド一覧、既存のWebDriver資産があるならSeleniumの仕組みと使い方が向きます。chromedpが向くのは、GoのバックエンドやCLIに、Chromeでの画面取得・PDF化・管理画面の自動操作を組み込む場合です。外部システムの操作を業務システムに組み込む開発は、フルスクラッチ開発でもご相談いただけます。
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のリリース一覧で確認してください。
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版などを選びます。