chromedp和headless-shell

5,399 阅读1分钟

chromedp

github.com/chromedp/ch…

package main

import (
	"context"
	"fmt"
	"github.com/chromedp/chromedp"
	"log"
	"time"
)

var timeOutError = errors.New("获取指定path的的页面元素超时")

func main() {
	dpCtx := newChromeDpCtx()
	defer func() {
		if err := chromedp.Cancel(dpCtx); err != nil {
			log.Println(err)
		}
	}()

	/** 调试时可以加上,避免主动关闭进程但是浏览器还在执行
	go func() {
		quit := make(chan os.Signal)
		signal.Notify(quit, os.Interrupt, os.Kill)
		<-quit
		_ = chromedp.Cancel(dpCtx)
		os.Exit(1)
	}()
	 */

	if err := chromedp.Run(dpCtx, newTask()); err != nil {
		log.Println("执行失败:", err)
		return
	}
}

//获得一个chromdp的context
func newChromeDpCtx() context.Context {
	options := []chromedp.ExecAllocatorOption{
		chromedp.Flag("headless", false), // debug使用false  正式使用用true
		chromedp.WindowSize(1280, 1024),  // 调整浏览器大小
	}
	options =   append(chromedp.DefaultExecAllocatorOptions[:], options...)
	ctx, _ := chromedp.NewExecAllocator(context.Background(), options...)
	ctx, _ = chromedp.NewContext(ctx, chromedp.WithLogf(log.Printf))  // 会打开浏览器并且新建一个标签页进行操作
    
    ctx, _ = chromedp.NewRemoteAllocator(ctx, "ws://127.0.0.1:9222/devtools/page/${targetId}") //使用远程调试,可以结合下面的容器使用
    ctx, _ = chromedp.NewContext(ctx, chromedp.WithLogf(log.Printf),chromedp.WithTargetID(${targetId}))// WithTargetID可以指定一个标签页进行操作
	return ctx
}

//返回一个任务的列队来执行
//这里示例登录网站后对一个div截图

func newTask() chromedp.Tasks {
	return chromedp.Tasks{
		toUrl("打开登录页面",`http://***.***.***.***:*****/#/login`),
		setValue("填写用户名", "//*[@id=\"app\"]/div/div/div[2]/form/div[1]/input", "******"),
		setValue("填写密码", "//*[@id=\"app\"]/div/div/div[2]/form/div[2]/input", "******"),
		click("点击登录按钮", "//*[@id=\"app\"]/div/div/div[2]/form/button"),
		toUrl("跳转至页面", "http://***.***.***.***:*****/#/project/1/dashboard/579"),
		chromedp.Sleep(2 * time.Second),
		screenShot("指定div截图","//*[@id=\"app\"]/div/div/div/div[2]/div/div/div/div/div[2]/div[1]/div"),
	}
}


func setValue(name, path string, value string) chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		defer handleActionError(name,&err)
		timeout, cancel := context.WithTimeout(ctx, 3*time.Second)
		defer cancel()
		if chromedp.WaitVisible(path).Do(timeout) != nil {
			return timeOutError
		}
		return chromedp.SetValue(path, value).Do(timeout)
	}
}

func click(name, path string) chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		defer handleActionError(name,&err)
		timeout, cancel := context.WithTimeout(ctx, 3*time.Second)
		defer cancel()
		if chromedp.WaitVisible(path).Do(timeout) != nil {
			return timeOutError
		}
		return chromedp.Click(path).Do(timeout)
	}
}

func toUrl(name, url string) chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		defer handleActionError(name,&err)
		chromedp.Sleep(1*time.Second).Do(ctx)
		return chromedp.Navigate(url).Do(ctx)
	}
}


func screenShot(name string,path string) chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		defer handleActionError(name,&err)
		timeout, cancel := context.WithTimeout(ctx, 30*time.Second)
		defer cancel()
		if chromedp.WaitVisible(path).Do(timeout) != nil {
			return timeOutError
		}
		var code []byte
		if err = chromedp.Screenshot(path, &code).Do(timeout); err != nil {
			return
		}
		return ioutil.WriteFile("shot.png", code, 0755)
	}
}

func handleActionError(name string,err *error){
	if err != nil {
		*err = fmt.Errorf("【%s】失败=>%w", name, *err)
	}
}

headless-shell

Chrome无头浏览器的容器,用于自动化/驱动web

https://registry.hub.docker.com/r/chromedp/headless-shell

$ docker run -d -p 9222:9222 --rm --name headless-shell chromedp/headless-shell

# if headless-shell is crashing with a BUS_ADRERR error, pass a larger shm-size:

$ docker run -d -p 9222:9222 --rm --name headless-shell --shm-size 2G chromedp/headless-shell

会缺少中文字体,进入容器,执行:

apt-get update &&  apt install xfonts-intl-chinese ttf-wqy-microhei xfonts-wqy

通过 http://ip:9222/json 来获取 调试模式下的浏览器tab id,可以通过 chromedp.NewRemoteAllocator来指定webSocketDebuggerUrl,就可以远程驱动谷歌浏览器进行操作了

图片.png

windows下 谷歌浏览器启动调试模式可以在快捷方式右键属性的目标后加入--remote-debugging-port 命令来完成,如:

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222

本地调试远程浏览器

http://localhost:9222/devtools/inspector.html?ws=${ip}:${port}/devtools/page/${targetId}