Loading...

文章背景图

chromedp:Go 语言直连 Chrome DevTools Protocol 库|部署与使用全指南

2026-11-06
0
- 字
- 分钟
|

chromedp 官方 Logo

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. 官方仓库链接

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 使用 cdproto v0.157.8,是新一代通用 + iterator API;旧版本 cdproto v0.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.com

7. 配置项

配置 作用 默认
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 生成、授权范围内的自动化任务。请尊重网页内容的版权,二次发布抓取结果需获得权利人许可。

评论交流

文章目录