golang实现Chrome调试协议类型安全绑定的插件库cdp的使用
Golang实现Chrome调试协议类型安全绑定的插件库cdp的使用
简介
cdp
包为Chrome DevTools协议(CDP)提供了类型安全的Go语言绑定。这些绑定是从最新的协议定义生成的,主要用于Google Chrome或Chromium,但也可以用于任何实现该协议的调试目标(如Node.js、Edge DevTools协议、Safari等)。
特性
- 可发现的Chrome DevTools协议API(GoDoc,自动完成友好)
- 上下文作为一等公民(用于超时和取消)
- 简单同步的事件处理(无回调)
- 并发安全
- 无静默或隐藏错误
- 尽可能匹配CDP类型到Go类型
- 关注点分离(避免混合CDP和RPC)
安装
$ go get -u github.com/mafredri/cdp
使用示例
下面是一个完整的示例,展示如何使用cdp库进行网页导航、获取DOM内容和截图:
package main
import (
"bufio"
"context"
"fmt"
"io/ioutil"
"log"
"os"
"time"
"github.com/mafredri/cdp"
"github.com/mafredri/cdp/devtool"
"github.com/mafredri/cdp/protocol/dom"
"github.com/mafredri/cdp/protocol/page"
"github.com/mafredri/cdp/rpcc"
)
func main() {
err := run(5 * time.Second)
if err != nil {
log.Fatal(err)
}
}
func run(timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// 使用DevTools HTTP/JSON API管理目标(如页面、webworkers)
devt := devtool.New("http://127.0.0.1:9222")
pt, err := devt.Get(ctx, devtool.Page)
if err != nil {
pt, err = devt.Create(ctx)
if err != nil {
return err
}
}
// 初始化到Chrome DevTools协议目标的新RPC连接
conn, err := rpcc.DialContext(ctx, pt.WebSocketDebuggerURL)
if err != nil {
return err
}
defer conn.Close() // 不关闭连接会导致内存泄漏
c := cdp.NewClient(conn)
// 打开DOMContentEventFired客户端来缓冲此事件
domContent, err := c.Page.DOMContentEventFired(ctx)
if err != nil {
return err
}
defer domContent.Close()
// 在Page域上启用事件,通常在启用事件前创建事件客户端更好,这样不会错过任何事件
if err = c.Page.Enable(ctx); err != nil {
return err
}
// 创建带有可选Referrer字段的Navigate参数
navArgs := page.NewNavigateArgs("https://www.google.com").
SetReferrer("https://duckduckgo.com")
nav, err := c.Page.Navigate(ctx, navArgs)
if err != nil {
return err
}
// 等待直到我们收到DOMContentEventFired事件
if _, err = domContent.Recv(); err != nil {
return err
}
fmt.Printf("页面加载完成,框架ID: %s\n", nav.FrameID)
// 获取文档根节点。我们可以在这里传递nil,因为此方法只接受可选参数
doc, err := c.DOM.GetDocument(ctx, nil)
if err != nil {
return err
}
// 获取页面的外部HTML
result, err := c.DOM.GetOuterHTML(ctx, &dom.GetOuterHTMLArgs{
NodeID: &doc.Root.NodeID,
})
if err != nil {
return err
}
fmt.Printf("HTML: %s\n", result.OuterHTML)
// 捕获当前页面的截图
screenshotName := "screenshot.jpg"
screenshotArgs := page.NewCaptureScreenshotArgs().
SetFormat("jpeg").
SetQuality(80)
screenshot, err := c.Page.CaptureScreenshot(ctx, screenshotArgs)
if err != nil {
return err
}
if err = ioutil.WriteFile(screenshotName, screenshot.Data, 0o644); err != nil {
return err
}
fmt.Printf("截图已保存: %s\n", screenshotName)
// 将页面保存为PDF
pdfName := "page.pdf"
f, err := os.Create(pdfName)
if err != nil {
return err
}
pdfArgs := page.NewPrintToPDFArgs().
SetTransferMode("ReturnAsStream") // 请求流
pdfData, err := c.Page.PrintToPDF(ctx, pdfArgs)
if err != nil {
return err
}
sr := c.NewIOStreamReader(ctx, *pdfData.Stream)
r := bufio.NewReader(sr)
// 以~r.Size()块写入文件
_, err = r.WriteTo(f)
if err != nil {
return err
}
err = f.Close()
if err != nil {
return err
}
fmt.Printf("PDF已保存: %s\n", pdfName)
return nil
}
主要功能说明
- 连接管理:通过
devtool
包获取WebSocket调试URL,然后使用rpcc
包建立连接 - 事件处理:通过
DOMContentEventFired
等事件监听页面状态变化 - 页面导航:使用
Page.Navigate
方法导航到指定URL - DOM操作:获取文档根节点和外部HTML
- 截图功能:捕获页面截图并保存为JPEG文件
- PDF导出:将页面导出为PDF文件
这个示例展示了cdp库的主要功能,包括页面控制、DOM操作和内容导出等常见用例。
更多关于golang实现Chrome调试协议类型安全绑定的插件库cdp的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现Chrome调试协议类型安全绑定的插件库cdp的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang中使用cdp库实现Chrome调试协议绑定
cdp是一个用于Golang的类型安全的Chrome DevTools Protocol (CDP)客户端库,它允许你通过代码控制Chrome或Chromium浏览器。下面我将介绍如何使用cdp库与Chrome浏览器交互。
安装cdp库
首先安装cdp库:
go get github.com/mafredri/cdp
go get github.com/mafredri/cdp/devtool
go get github.com/mafredri/cdp/protocol
go get github.com/mafredri/cdp/rpcc
基本使用示例
1. 启动Chrome浏览器
首先确保Chrome浏览器以远程调试模式运行:
google-chrome --remote-debugging-port=9222
2. 连接浏览器示例
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/mafredri/cdp"
"github.com/mafredri/cdp/devtool"
"github.com/mafredri/cdp/protocol/page"
"github.com/mafredri/cdp/rpcc"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 连接到浏览器
devt := devtool.New("http://localhost:9222")
pt, err := devt.Get(ctx, devtool.Page)
if err != nil {
pt, err = devt.Create(ctx)
if err != nil {
log.Fatal(err)
}
}
// 建立RPC连接
conn, err := rpcc.DialContext(ctx, pt.WebSocketDebuggerURL)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 创建CDP客户端
client := cdp.NewClient(conn)
// 启用必要的域
if err := client.Page.Enable(ctx); err != nil {
log.Fatal(err)
}
// 导航到页面
navArgs := page.NewNavigateArgs("https://example.com")
_, err = client.Page.Navigate(ctx, navArgs)
if err != nil {
log.Fatal(err)
}
// 等待页面加载完成
loadEventFired, err := client.Page.LoadEventFired(ctx)
if err != nil {
log.Fatal(err)
}
defer loadEventFired.Close()
_, err = loadEventFired.Recv()
if err != nil {
log.Fatal(err)
}
// 获取页面标题
title, err := client.Page.GetTitle(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Page title: %s\n", title.Title)
}
高级功能示例
1. 截取屏幕截图
func takeScreenshot(client *cdp.Client, ctx context.Context) error {
// 获取视口尺寸
metrics, err := client.Page.GetLayoutMetrics(ctx)
if err != nil {
return err
}
// 设置截图参数
screenshotArgs := page.NewCaptureScreenshotArgs().
SetFormat("png").
SetQuality(90).
SetClip(&page.Viewport{
X: metrics.LayoutViewport.ClientX,
Y: metrics.LayoutViewport.ClientY,
Width: metrics.LayoutViewport.ClientWidth,
Height: metrics.LayoutViewport.ClientHeight,
Scale: 1,
})
// 截取屏幕
screenshot, err := client.Page.CaptureScreenshot(ctx, screenshotArgs)
if err != nil {
return err
}
// 保存截图
err = os.WriteFile("screenshot.png", screenshot.Data, 0644)
if err != nil {
return err
}
fmt.Println("Screenshot saved as screenshot.png")
return nil
}
2. 执行JavaScript代码
func evaluateJS(client *cdp.Client, ctx context.Context, js string) (string, error) {
evalArgs := page.NewEvaluateArgs(js)
result, err := client.Page.Evaluate(ctx, evalArgs)
if err != nil {
return "", err
}
if result.Result.Value != nil {
return fmt.Sprintf("%v", result.Result.Value), nil
}
return "", nil
}
// 使用示例
// result, err := evaluateJS(client, ctx, "document.querySelector('h1').textContent")
3. 监听控制台日志
func listenConsoleLogs(client *cdp.Client, ctx context.Context) error {
// 启用Console域
if err := client.Console.Enable(ctx); err != nil {
return err
}
// 监听控制台消息
consoleEvents, err := client.Console.MessageAdded(ctx)
if err != nil {
return err
}
go func() {
defer consoleEvents.Close()
for {
event, err := consoleEvents.Recv()
if err != nil {
return
}
fmt.Printf("Console %s: %s\n", event.Message.Level, event.Message.Text)
}
}()
return nil
}
最佳实践
- 上下文管理:始终使用context来控制超时和取消操作
- 资源清理:确保关闭所有连接和监听器
- 错误处理:妥善处理所有可能的错误
- 并发安全:cdp客户端不是并发安全的,需要自己管理同步
总结
cdp库为Golang提供了类型安全的Chrome DevTools Protocol绑定,使得自动化浏览器操作变得简单可靠。通过这个库,你可以实现页面导航、DOM操作、JavaScript执行、网络监控等各种浏览器自动化功能。
更多高级用法可以参考cdp官方文档和Chrome DevTools Protocol文档。