chromedpheadless-chrome

"无头"Chrome与远程调式协议

headless-chrome
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --dump-dom https://www.sogou.com
Remote Debugging Protocol
chromedp

使用方法和注意事项

现在网上很多例子都是只爬取一个页面的例子,然而实际中我们经常需要按一定规则爬取整个网站,这时候就涉及到Chrome实例的复用问题,总不能跟其他博客说的那样每爬取一个网页都要销毁、打开Chrome吧,效率太低了。

User-Agent
	options := []chromedp.ExecAllocatorOption{
		chromedp.Flag("headless", false), // debug使用
		chromedp.Flag("blink-settings", "imagesEnabled=false"),
		chromedp.UserAgent(`Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36`),
	}
	options = append(chromedp.DefaultExecAllocatorOptions[:], options...)

Chrome初始化代码如下:

c, _ := chromedp.NewExecAllocator(context.Background(), options...)

	// create context
	chromeCtx, cancel := chromedp.NewContext(c, chromedp.WithLogf(log.Printf))
	// 执行一个空task, 用提前创建Chrome实例
	chromedp.Run(chromeCtx, make([]chromedp.Action, 0, 1)...)

可以看到chromedp是严重依赖于go的context包的,如果不熟悉context的使用最好先去研究一下。

Run()ActionRun()

完成初始化后,还有一个重要的设置就是一定要设置超时时间,否则很容易卡住:

// 给每个页面的爬取设置超时时间
	timeoutCtx, cancel := context.WithTimeout(chromeContext, 20 * time.Second)
	defer cancel()

然后,就可以向无头浏览器发送指令了:

log.Printf("Chrome visit page %s\n", link)

	var htmlContent string
	err := chromedp.Run(timeoutCtx,
		chromedp.Navigate(link),
		chromedp.WaitVisible(waitExpression),
		chromedp.OuterHTML(`document.querySelector("body")`, &htmlContent, chromedp.ByJSPath),
	)
waitExpressionhtmlContentgoquery
waitExpression
body > div.aw-container-wrap
bodyaw-container-wrap
chromeCtx
timeoutCtx, cancel := context.WithTimeout(chromeContext, 20 * time.Second)

这样就不会销毁Chrome进程再重新创建一遍了。

有了这个神器,爬天爬地爬空气都不怕,只是慢了点而已。