部署 chrome-headless-shel l 以及使用 chromedp 实现自动化操作
一、使用 chromedp 操作本地 谷歌浏览器
第三方库:https://github.com/chromedp
代码实现:
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/chromedp/cdproto/dom"
"github.com/chromedp/chromedp"
)
func main() {
var url = "https://www.doc88.com/p-99929140776272.html?s=rel&id=9"
var timeOut = time.Second * 60 * 5
//var remoteAllocatorUrl = "ws://0.0.0.0:9222" // 操作远程的 chrome 浏览器
var remoteAllocatorUrl = "" // 空则表示操作本地的谷歌浏览器
var err error
// 创建上下文和取消函数
ctx, cancel := chromedp.NewContext(context.Background())
defer func() {
cancel()
}()
// 设置超时时间
ctx, cancel = context.WithTimeout(ctx, timeOut)
defer func() {
cancel()
}()
var execCtx context.Context
var cancelExec context.CancelFunc
// 创建chrome配置
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", false), // 使用不展示浏览器,
chromedp.WindowSize(1200, 1200), // 打开的浏览器的窗口大小
)
// 创建chrome执行器
if remoteAllocatorUrl != "" {
execCtx, cancelExec = chromedp.NewRemoteAllocator(ctx, remoteAllocatorUrl, []chromedp.RemoteAllocatorOption{}...)
} else {
execCtx, cancelExec = chromedp.NewExecAllocator(ctx, opts...)
}
defer func() {
cancelExec()
}()
// 创建chromedp任务上下文
taskCtx, cancelTask := chromedp.NewContext(execCtx)
defer cancelTask()
defer cancelTask()
fmt.Println("chromedp.Navigate:", url)
var buf []byte
runAction := []chromedp.Action{
chromedp.Navigate(url),
}
if remoteAllocatorUrl != "" {
runAction = append(runAction, chromedp.EmulateViewport(1920, 1080, chromedp.EmulateScale(1)))
}
err = chromedp.Run(taskCtx,
runAction...,
)
if err != nil {
fmt.Printf("Run %s err:%v\n", url, err)
return
}
// 持续点击id='continueButton',直到页面不存在为止
fmt.Println("chromedp.continueButton:")
for i := 1; i <= 10; i++ {
err = chromedp.Run(taskCtx,
chromedp.Evaluate(`document.querySelector("#continueButton").click()`, nil),
chromedp.Sleep(time.Second*1),
)
fmt.Println("click ontinueButton:", i)
}
// 获取第一个div的位置并滚动到该位置
var divPos *dom.Rect
err = chromedp.Run(taskCtx,
chromedp.Evaluate(`document.querySelector('.outer_page').getBoundingClientRect()`, &divPos),
)
if err != nil {
return
}
time.Sleep(time.Second * 1)
// 创建一个空的结果变量,用于存储获取到的div数量
var divCount int
// 获取div的数量
err = chromedp.Run(taskCtx,
chromedp.EvaluateAsDevTools(`document.querySelectorAll('.outer_page').length`, &divCount),
chromedp.Sleep(time.Second*1),
chromedp.ScrollIntoView("#readEndDiv"),
chromedp.Sleep(time.Second*3),
chromedp.ScrollIntoView(".doctopic"),
chromedp.Sleep(time.Second*1),
chromedp.CaptureScreenshot(&buf), // 截图
)
if err != nil {
return
}
fmt.Println("chromedp.get div count:", divCount)
time.Sleep(time.Second * 1)
// 循环点击id='nextPageButton'并下载图片
for i := 1; i <= divCount; i++ {
err = chromedp.Run(taskCtx,
chromedp.Click("#nextPageButton"),
chromedp.WaitVisible(".outer_page"),
)
err = chromedp.Run(taskCtx,
chromedp.WaitVisible(fmt.Sprintf("#pagepb_%d", i), chromedp.ByID),
chromedp.ActionFunc(func(ctx context.Context) error {
var pageContent = "内容加载中......"
for pageContent != "" {
err = chromedp.TextContent(fmt.Sprintf("#pagepb_%d", i), &pageContent).Do(ctx)
if err != nil {
break
}
time.Sleep(time.Second * 1)
fmt.Printf("cur page %d load pageContent: %s\n", i, pageContent)
}
return nil
}),
chromedp.ActionFunc(func(ctx context.Context) error {
jsCode := fmt.Sprintf(`document.querySelector('#page_%d').toDataURL('image/jpeg', 0.8)`, i)
var result string
err = chromedp.Evaluate(jsCode, &result).Do(ctx)
if err != nil {
return err
}
fmt.Println("download result:", fmt.Sprintf("%d.jpg", i))
return nil
}),
// chromedp.Sleep(time.Second*1),
)
if err != nil {
return
}
fmt.Println("nextPageButton download ", i)
// 添加延迟
//time.Sleep(1 * time.Second)
}
err = os.WriteFile("screenshot.png", buf, 0644)
if err != nil {
return
}
fmt.Println("chromedp.Navigate:", url)
return
}
二、 操作远程的 Chrome 浏览器
1. 在服务器部署 chrome-headless-shell
参考资料:
https://github.com/chromedp/docker-headless-shell
examples/remote/main.go at master · chromedp/examples · GitHub
https://juejin.cn/post/6959511879936901133
1). 使用 docker 部署
# 启动容器
docker run -d -p 9222:9222 --rm --name headless-shell --shm-size 2G chromedp/headless-shell
# 默认是没有中文字体的,所以需要下载中文字体
docker exec -it headless-shell apt-get update
docker exec -it headless-shell apt-get install -y fonts-wqy-zenhei
docker exec -it headless-shell apt install xfonts-intl-chinese ttf-wqy-microhei xfonts-wqy
# 重启容器
docker restart headless-shell
2). 使用 k8s 部署
只需要编写 YAML ,然后 kubectl -f xxx.yaml
参考:/init/chromeheadlessshell/chromeheadlessshell.yaml
2. 实现代码
和步骤一的代码一直,只需要设置:
var remoteAllocatorUrl = "ws://0.0.0.0:9222" // 操作远程的 Chrome 浏览器