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进程再重新创建一遍了。

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