Go chromedp
最后修改时间 2024 年 4 月 11 日
在本文中,我们将展示如何在 Golang 中使用 chromedp 自动化浏览器。
chromedp 是一个 Go 库,它提供了一个高级 API,用于通过 DevTools Protocol 控制 Chromium。它允许在无头模式(默认模式)下使用浏览器,这种模式无需用户界面即可运行。这对于编写脚本非常有用。
获取外层 HTML
chromedp.OuterHTML
会检索与选择器匹配的第一个元素节点的外部 HTML。
package main import ( "context" "fmt" "log" "github.com/chromedp/chromedp" ) func main() { ctx, cancel := chromedp.NewContext(context.Background()) defer cancel() url := "http://webcode.me" var data string if err := chromedp.Run(ctx, chromedp.Navigate(url), chromedp.OuterHTML("html", &data, chromedp.ByQuery), ); err != nil { log.Fatal(err) } fmt.Println(data) }
该示例检索 webcode.me 的主页。
ctx, cancel := chromedp.NewContext(context.Background()) defer cancel()
chromedp.NewContext
从父上下文创建一个 chromedp 上下文。返回的取消函数必须被调用以终止 chromedp 上下文;该函数等待资源清理完毕,并返回在此过程中遇到的任何错误。
if err := chromedp.Run(ctx, chromedp.Navigate(url), chromedp.OuterHTML("html", &data, chromedp.ByQuery), ); err != nil { log.Fatal(err) }
Run
函数针对上下文执行操作。我们导航到 URL 并检索 html
标签的 HTML 数据。
获取标题
使用 chromedp.Title
,我们可以获取文档的标题。
package main import ( "context" "fmt" "io" "log" "net/http" "net/http/httptest" "strings" "github.com/chromedp/chromedp" ) func writeHTML(content string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") io.WriteString(w, strings.TrimSpace(content)) }) } func main() { ctx, cancel := chromedp.NewContext(context.Background()) defer cancel() ts := httptest.NewServer(writeHTML(` <head> <title>Home page</title> </head> <body> <p>Hello there!</a> </body> `)) defer ts.Close() var title string if err := chromedp.Run(ctx, chromedp.Navigate(ts.URL), chromedp.Title(&title), ); err != nil { log.Fatal(err) } fmt.Println(title) }
在此示例中,我们创建了一个内置的网络服务器,它发送一个简单的网页。然后我们获取其标题。
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") io.WriteString(w, strings.TrimSpace(content)) })
处理函数将 text/html
内容发送回客户端。
ts := httptest.NewServer(writeHTML(` <head> <title>Home page</title> </head> <body> <p>Hello there!</a> </body> `))
使用 httptest.NewServer
创建了一个测试服务器。
defer ts.Close()
在程序结束时,测试服务器会被关闭。
if err := chromedp.Run(ctx, chromedp.Navigate(ts.URL), chromedp.Title(&title), ); err != nil { log.Fatal(err) }
我们导航到测试服务器的 URL,并使用 chromedp.Title
获取文档标题。
$ go run title.go Home page
设置超时
我们可能会遇到任务死锁。为了防止这种情况,我们可以设置超时。
package main import ( "context" "fmt" "log" "strings" "time" "github.com/chromedp/chromedp" ) func main() { ctx, cancel := chromedp.NewContext(context.Background()) defer cancel() ctx, cancel = context.WithTimeout(ctx, 5*time.Second) defer cancel() url := "http://webcode.me" var res string err := chromedp.Run(ctx, chromedp.Navigate(url), chromedp.Text("body", &res, chromedp.NodeVisible), ) if err != nil { log.Fatal(err) } fmt.Println(strings.TrimSpace(res)) }
在此示例中,我们获取 body
标签的可见文本。并设置了超时。
ctx, cancel = context.WithTimeout(ctx, 5*time.Second) defer cancel()
我们将超时设置为 5 秒。
err := chromedp.Run(ctx, chromedp.Navigate(url), chromedp.Text("body", &res, chromedp.NodeVisible), )
我们运行一个任务列表。我们使用 chromedp.Text
获取 body
的文本。
点击操作
使用 chromedp.Click
执行点击查询操作。
package main import ( "context" "log" "time" "github.com/chromedp/chromedp" "github.com/chromedp/chromedp/device" ) func main() { ctx, cancel := chromedp.NewContext( context.Background(), ) defer cancel() ctx, cancel = context.WithTimeout(ctx, 15*time.Second) defer cancel() url := "http://webcode.me/click.html" var ua string err := chromedp.Run(ctx, chromedp.Emulate(device.IPhone11), chromedp.Navigate(url), chromedp.Click("button", chromedp.NodeVisible), chromedp.Text("#output", &ua), ) if err != nil { log.Fatal(err) } log.Printf("User agent: %s\n", ua) }
在此示例中,我们点击了一个网页上的按钮。该网页在输出 div 中显示了客户端的用户代理。
err := chromedp.Run(ctx, chromedp.Emulate(device.IPhone11), chromedp.Navigate(url), chromedp.Click("button", chromedp.NodeVisible), chromedp.Text("#output", &ua), )
在任务列表中,我们导航到 URL,点击按钮,然后检索输出文本。我们获取了用户代理。我们使用 chromedp.Emulate
模拟了 IPhone11 设备。
创建屏幕截图
我们可以使用 chromedp.Screenshot
创建元素的屏幕截图。chromedp.FullScreenshot
则会截取整个浏览器视口的屏幕截图。
package main import ( "context" "fmt" "io/ioutil" "log" "github.com/chromedp/chromedp" ) func main() { ctx, cancel := chromedp.NewContext( context.Background(), ) defer cancel() url := "http://webcode.me" var buf []byte if err := chromedp.Run(ctx, ElementScreenshot(url, "body", &buf)); err != nil { log.Fatal(err) } if err := ioutil.WriteFile("body.png", buf, 0o644); err != nil { log.Fatal(err) } if err := chromedp.Run(ctx, FullScreenshot(url, 90, &buf)); err != nil { log.Fatal(err) } if err := ioutil.WriteFile("full.png", buf, 0o644); err != nil { log.Fatal(err) } fmt.Println("screenshots created") } func ElementScreenshot(url, sel string, res *[]byte) chromedp.Tasks { return chromedp.Tasks{ chromedp.Navigate(url), chromedp.Screenshot(sel, res, chromedp.NodeVisible), } } func FullScreenshot(url string, quality int, res *[]byte) chromedp.Tasks { return chromedp.Tasks{ chromedp.Navigate(url), chromedp.FullScreenshot(res, quality), } }
我们创建了两个屏幕截图。图像被写入磁盘,使用 ioutil.WriteFile
。
提交表单
chromedp.SendKeys
用于填充输入字段。表单可以使用 chromedp.Click
或 chromedp.Submit
进行提交。
package main import ( "context" "fmt" "log" "time" "github.com/chromedp/chromedp" ) func main() { ctx, cancel := chromedp.NewContext( context.Background(), ) defer cancel() ctx, cancel = context.WithTimeout(ctx, 6*time.Second) defer cancel() url := "http://webcode.me/submit/" var res string err := chromedp.Run(ctx, chromedp.Navigate(url), chromedp.SendKeys("input[name=name]", "Lucia"), chromedp.SendKeys("input[name=message]", "Hello!"), // chromedp.Click("button", chromedp.NodeVisible), chromedp.Submit("input[name=name]"), chromedp.Text("*", &res), ) if err != nil { log.Fatal(err) } fmt.Println(res) }
该示例填写了一个表单并接收了一条消息。
chromedp.SendKeys("input[name=name]", "Lucia"), chromedp.SendKeys("input[name=message]", "Hello!"),
我们将两个字符串设置到指定的输入标签中。
// chromedp.Click("button", chromedp.NodeVisible), chromedp.Submit("input[name=name]"),
我们可以使用 chromedp.Click
或 chromedp.Submit
来提交表单。在后一种情况下,该函数会提交与选择器匹配的第一个元素节点的父表单。
来源
在本文中,我们已经使用 chromedp 在 Go 中自动化了浏览器。