chromedp:Go 语言直连 Chrome DevTools Protocol 库|部署与使用全指南
chromedp 是一个 Go 包,通过 Chrome DevTools Protocol 直接驱动 Chrome / Chromium,不需要外部 driver。本文按官方 README 给出 go get 安装、最小示例、远程浏览器接入、headless-shell Docker 镜像与常见问题。
1. 项目简介与定位
chromedp 是一个 Go 语言包,用于从 Go 程序里驱动讲 Chrome
DevTools Protocol 的浏览器。它不需要 chromedriver / geckodriver
之类的外部 driver——它直接通过 pipe(或 WebSocket)与 Chrome
通信。模块只依赖 Go 标准库与 cdproto,启动浏览器后通过 pipe
与之对话;需要 WebSocket 的代码在子包
github.com/chromedp/chromedp/remote。
1.1 核心信息一览
| 项 | 内容 |
|---|---|
| 项目名 | chromedp |
| 仓库 | https://github.com/chromedp/chromedp |
| Go 文档 | https://pkg.go.dev/github.com/chromedp/chromedp |
| 示例集 | https://github.com/chromedp/examples |
| 主语言 | Go |
| Go 版本 | 1.25+ |
| 控制协议 | Chrome DevTools Protocol |
| 外部 driver | 不需要 |
| 许可证 | MIT |
| 部署形态 | Go module / Docker 镜像 chromedp/headless-shell |
1.2 适用场景
- 用 Go 写后端服务,需要 headless Chrome 渲染页面、截图、打印 PDF
- 高并发抓取:一个 Chrome 实例里开多个 tab,goroutine 池分发任务
- 把 headless Chrome 塞进 Docker(
chromedp/headless-shell镜像比完整 Chrome 小很多) - 直接调 CDP 原语(网络拦截、HAR、console、权限、地理位置)做深度定制
1.3 能力边界(官方口径)
chromedp 只支持讲 CDP 的浏览器(Chrome / Chromium /
headless-shell)。Firefox / Safari
不在支持范围内。它是库,不带测试
runner、不带断言框架。默认通过 pipe 启动并独占一个 Chrome
子进程;要连接已存在的浏览器(容器、远程服务),用
remote.NewAllocator。
2. 开源协议
chromedp 采用 MIT License。
3. 官方仓库链接
- 仓库:https://github.com/chromedp/chromedp
- Go 包文档:https://pkg.go.dev/github.com/chromedp/chromedp
- API 说明:https://github.com/chromedp/chromedp/blob/main/docs/API.md
- 迁移指南:https://github.com/chromedp/chromedp/blob/main/docs/MIGRATION.md
- 示例集(45 个 main.go):https://github.com/chromedp/examples
- CDP 协议类型包:https://pkg.go.dev/github.com/chromedp/cdproto
- Docker 镜像:https://hub.docker.com/r/chromedp/headless-shell/
- Discord 讨论:https://discord.gg/WDWAgXwJqN
4. 前置要求
| 依赖 | 版本要求 | 说明 |
|---|---|---|
| Go | 1.25 或更新 | README 明确要求;JSON 类型在 1.27+ 用标准库
encoding/json/v2,1.25/1.26 需
GOEXPERIMENT=jsonv2 |
| Chrome / Chromium | 任一现代版本 | chromedp 默认会自动找系统里的 Chrome;容器里用
chromedp/headless-shell |
| Linux 显示环境(可选) | DISPLAY 或 WAYLAND_DISPLAY |
仅当要看到浏览器窗口时需要;纯 headless 不需要 |
| Docker(可选) | 任意较新版本 | 跑 chromedp/headless-shell 镜像 |
注意:README 指出 v0.20.0 使用
cdprotov0.157.8,是新一代通用 + iterator API;旧版本cdprotov0.157.0~v0.157.2 是旧 API。升级前先看docs/MIGRATION.md。
5. 安装部署
5.1 go get 安装
go get -u github.com/chromedp/chromedp模块只依赖 Go 标准库与 cdproto。需要 WebSocket
连接已存在的浏览器时,再 import 子包:
import "github.com/chromedp/chromedp/remote"5.2 在容器里跑:chromedp/headless-shell 镜像
chromedp/headless-shell 是一个更小的 headless Chrome
构建,chromedp 默认就能在 PATH 里找到它。直接把你的 Go
程序打进镜像:
FROM chromedp/headless-shell:latest
COPY your-app /usr/local/bin/
ENTRYPOINT ["your-app"]README 里的 multi 示例就是用 headless-shell
镜像在容器里跑 chromedp;distrust 示例则演示如何在容器里用
Podman 启动 headless-shell 并验证证书。
5.3 连接已存在的 Chrome(远程 / 容器)
如果你已经有一个带 --remote-debugging-port=9222 的
Chrome 实例,用 remote.NewAllocator 连过去:
allocCtx, cancel := remote.NewAllocator(context.Background(), "ws://127.0.0.1:9222/")
defer cancel()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
title, err := chromedp.Run(ctx, chromedp.Title())6. 最小示例代码
6.1 打开页面并打印标题
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://pkg.go.dev/`)); err != nil {
log.Fatal(err)
}
title, err := chromedp.Run(ctx, chromedp.Title())
if err != nil {
log.Fatal(err)
}
fmt.Println(title)
}要点:
chromedp.Do跑不返回值的 action;chromedp.Run跑一个 action 并返回它的值。- 第一个
Run/Do会启动浏览器,并把浏览器生命周期绑到该 context 上。
6.2 事件迭代器(新 API)
loaded := chromedp.Events(ctx, page.LoadEventFired)
if err := chromedp.Do(ctx, chromedp.Navigate(`https://pkg.go.dev/`)); err != nil {
log.Fatal(err)
}
for ev, err := range loaded {
if err != nil {
log.Fatal(err)
}
fmt.Println(ev.Timestamp)
break
}6.3 多 tab 并发(goroutine 池)
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
if err := chromedp.Do(ctx); err != nil { // 启动浏览器
log.Fatal(err)
}
var wg sync.WaitGroup
for _, url := range urls {
wg.Go(func() {
tabCtx, cancel := chromedp.NewContext(ctx) // 新 tab
defer cancel()
if err := chromedp.Do(tabCtx, chromedp.Navigate(url)); err != nil {
log.Println(url, err)
}
})
}
wg.Wait()6.4 截图与 PDF
README 的 examples 仓库提供 screenshot(整页 /
元素截图)、pdf(打印 A4
PDF)、pdfoptions、pdfstream、screencast(逐帧
JPEG)等完整可运行程序:
go run github.com/chromedp/examples/screenshot@latest -url https://example.com
go run github.com/chromedp/examples/pdf@latest -url https://example.com7. 配置项
| 配置 | 作用 | 默认 |
|---|---|---|
chromedp.WithVisibleWindow() |
显示浏览器窗口 | 默认 headless |
CHROMEDP_VISIBLEWINDOW=1 |
环境变量,等价于上面的选项 | 未设置 |
chromedp.Flag("disable-dev-shm-usage", ...) |
共享内存走 /tmp,避免容器 /dev/shm 过小崩溃 | 默认开启 |
chromedp.Flag("disable-features", "SitePerProcess,IsolateOrigins") |
关闭站点隔离,便于访问跨站 iframe | 关闭(保持安全) |
remote.WebSocket |
用 WebSocket 而不是 pipe 与 Chrome 通信 | 默认 pipe |
remote.WithKeepOpen |
程序退出后保留浏览器窗口 | 默认退出即关 |
CHROMEDP_* |
其他 allocator 选项见 DefaultExecAllocatorOptions |
— |
8. 常见问题
Q1:看不到 Chrome 窗口? A:chromedp 默认
headless。加 chromedp.WithVisibleWindow(),或设环境变量
CHROMEDP_VISIBLEWINDOW=1。Linux 下可见窗口需要
DISPLAY / WAYLAND_DISPLAY。
Q2:报 context canceled?
A:与浏览器的连接断了——可能是浏览器被人手关了、被 OOM kill 了、或第一个
Run 用了
context.WithTimeout,超时后浏览器一起被回收。正确做法:先用一个不带超时的
context 启动浏览器,再派生带超时的子 context 去跑单个 action。
Q3:Chrome 一退出 Go 程序就跟着关? A:Linux 下
chromedp 会清理它启动的子进程。要保留浏览器,用
remote.WithKeepOpen,或自己启动 Chrome 后用
remote.NewAllocator 连过去。
Q4:跨站 iframe 里找不到元素? A:跨站 iframe 在
Chrome 里是独立进程 / 独立 target。用 chromedp.Targets(ctx)
找到 Type == "iframe" 的 target,再
chromedp.NewContext(ctx, chromedp.WithTargetID(info.TargetID))
挂上去。或者显式
Flag("disable-features", "SitePerProcess,IsolateOrigins")
关闭站点隔离(仅用于可信页面)。
Q5:容器里 Chrome 崩溃? A:默认 allocator 已经开了
disable-dev-shm-usage;如果还崩,确认镜像是
chromedp/headless-shell 或装全了 Chrome 系统依赖,并把
/dev/shm 挂大一点。
Q6:goroutine 能不能共用一个 context?
A:不要。一个 context 对应一个 tab,多个 goroutine
在同一 context 上 Navigate 会竞争。正确做法:父 context 先
chromedp.Do(ctx) 起浏览器,再为每个 goroutine
chromedp.NewContext(parent) 开新 tab。
9. 与同类工具对比
| 维度 | chromedp | Rod | Playwright Go | Selenium (Go) |
|---|---|---|---|---|
| 语言 | 仅 Go | 仅 Go | 仅 Go(社区移植) | Python/Java/JS/… |
| 协议 | CDP 直连 | CDP 直连 | 自家 WebSocket | W3C WebDriver |
| 外部 driver | 不需要 | 不需要 | 不需要 | 需要 chromedriver |
| 风格 | action 链式 + iterator | 链式 超直观 | 类 Playwright API | 标准 WebDriver |
| 容器镜像 | 官方 chromedp/headless-shell |
自带 | 自带 | 需自备 |
| 生态成熟度 | 高,10 年+ | 高 | 中 | 高 |
chromedp 与 Rod 都是 Go + CDP 路线:chromedp 更贴近协议本身、API 偏底层但稳定,Rod 封装更友好。Selenium Go 绑定走 W3C WebDriver,需要外部 driver,与 Selenium 生态互通。
10. 许可
MIT License。详见 https://github.com/chromedp/chromedp/blob/main/LICENSE 。
11. 责任使用声明
使用 chromedp 自动化访问任何站点时,请遵守目标站点的
robots.txt 与服务条款(ToS)。请合理控制 goroutine
并发与页面打开频率,在请求之间加入延时,避免对目标服务器造成压力。请勿用
chromedp
绕过登录验证、付费墙、验证码或反爬系统。请勿批量抓取、存储或传播个人隐私数据。本工具仅应用于公开数据的合法研究、自身服务的页面渲染与
PDF
生成、授权范围内的自动化任务。请尊重网页内容的版权,二次发布抓取结果需获得权利人许可。