chromedp仿真爬虫

介绍

golang仿真爬虫包

功能

完全模拟人工操作

支持Chrome DevTools Protocol

用途

自动化测试、爬虫、自动签到等等人工能完成的所有操作

反爬策略绕过方式

常见的验证方式:

  1. 扫码登陆
  2. 短信验证
  3. 图形验证
  4. 点击验证
  5. 其他

绕过思路:

人工完成验证

缺点

比较吃资源,因为chrome本身比较吃内存。

使用入门

浏览器初始化默认参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var DefaultExecAllocatorOptions = [...]ExecAllocatorOption{
NoFirstRun,
NoDefaultBrowserCheck,
Headless,

Flag("disable-background-networking", true),
Flag("enable-features", "NetworkService,NetworkServiceInProcess"),
Flag("disable-background-timer-throttling", true),
Flag("disable-backgrounding-occluded-windows", true),
Flag("disable-breakpad", true),
Flag("disable-client-side-phishing-detection", true),
Flag("disable-default-apps", true),
Flag("disable-dev-shm-usage", true),
Flag("disable-extensions", true),
Flag("disable-features", "site-per-process,Translate,BlinkGenPropertyTrees"),
Flag("disable-hang-monitor", true),
Flag("disable-ipc-flooding-protection", true),
Flag("disable-popup-blocking", true),
Flag("disable-prompt-on-repost", true),
Flag("disable-renderer-backgrounding", true),
Flag("disable-sync", true),
Flag("force-color-profile", "srgb"),
Flag("metrics-recording-only", true),
Flag("safebrowsing-disable-auto-update", true),
Flag("enable-automation", true), // 会显示自动化测试标识
Flag("password-store", "basic"),
Flag("use-mock-keychain", true),
}

用前须知

chromedp使用context参数传递上下文,用以区分不同浏览器,同时将操作串联成线。

chromedp需要初始化,主要指定浏览器运行时的一些参数,以及何时回收资源(关闭浏览器)。

chromedp使用run方法,传递符合chromedp.Action接口的动作,以调用这些动作。

常用功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
chromedp.NewContext() 初始化chromedp的上下文,后续这个页面都使用这个上下文进行操作

chromedp.Run() 运行一个chrome的一系列操作

chromedp.Navigate() 将浏览器导航到某个页面

chromedp.Title() 检索页面title

chromedp.WaitVisible() 等候某个元素可见,再继续执行。

chromedp.WaitNotVisible(`#docsubject`, chromedp.ByID) 等元素消失时

chromedp.Click() 模拟鼠标点击某个元素

chromedp.Value() 获取某个元素的value值

chromedp.ActionFunc() 再当前页面执行某些自定义函数

chromedp.Text() 读取某个元素的text值

chromedp.Evaluate() 执行某个js,相当于控制台输入js

network.SetExtraHTTPHeaders() 截取请求,额外增加header头

chromedp.SendKeys() 模拟键盘操作,输入字符

chromedp.Nodes() 根据xpath获取某些元素,并存储进入数组

chromedp.NewRemoteAllocator

chromedp.OuterHTML() 获取元素的outer html

chromedp.Screenshot() 根据某个元素截图

page.CaptureScreenshot() 截取整个页面的元素

chromedp.Submit() 提交某个表单

chromedp.WaitNotPresent() 等候某个元素不存在,比如“正在搜索。。。”

chromedp.Stop() 停止网页加载(不停止的话,有时会长时间加载)

参考:

https://github.com/chromedp/chromedp

https://www.baidu.com/s?ie=UTF-8&wd=chromedp

简单调试

分解动作,动作之间穿插输出,判断程序运行状况。

使用步骤

开启gomod支持

go env -w GO111MODULE=on

go env -w GOPROXY=https://goproxy.io,direct

示例:访问百度,并搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"context"
"fmt"

"github.com/chromedp/chromedp"
)

func main() {
ctx, _ := chromedp.NewExecAllocator(
context.Background(),

// 以默认配置的数组为基础,覆写headless参数
// 当然也可以根据自己的需要进行修改,这个flag是浏览器的设置
append(
chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", false), // 显示界面
)...,
)
ctx, _ = chromedp.NewContext(
ctx,
)

chromedp.Run(ctx, chromedp.Navigate("http://www.baidu.com"))
chromedp.Run(ctx, chromedp.SendKeys(`#kw`, "111", chromedp.ByID))
fmt.Println("等待两秒")
chromedp.Run(ctx, chromedp.WaitVisible(`#su`, chromedp.ByID))
//chromedp.Run(ctx, chromedp.Click(`document.querySelector("#su")`, chromedp.ByJSPath))
chromedp.Run(ctx, chromedp.Click(`document.querySelector("#su")`, chromedp.ByJSPath))
//chromedp.Run(ctx, chromedp.Click(`#su`, chromedp.ByID))

}

注意点

注意点一

可以使用chromedp.Flag("headless", false)参数显示浏览器,默认是隐藏的,然而从实践中得知,初始化时只要指定headless参数,不管是chromedp.Flag("headless", false)还是chromedp.Flag("headless", true),chrome都会显示,除非不指定headless参数,浏览器才会隐藏。

从github的issue页面中得知,当启用headless隐藏浏览器时,此时user-agent会发生变化(添加了headless标识),应该在初始化时手动指定user-agent。

从github的issue页面中得知,如果想对google的一些资产进行自动化测试,应该指定enable-automation参数为false,关闭自动化测试标识符,此时浏览器将不再显示”Chrome正受到自动化测试软件的控制。”字样,如下图:

截图

注意点二

chromedp无法处理Alert的弹窗内容,如果页面出现Alert,将会导致页面永久加载下去,chromedp将无法进行其他动作,解决这个问题有两个方法:

方法一:

手动关闭弹窗,chromedp将会恢复

方法二:

使用chromedp操控js控制台,屏蔽弹窗,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
// create context
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()

// run task list
var res interface{}
err := chromedp.Run(ctx,
chromedp.Navigate(`https://www.quackit.com/javascript/javascript_alert_box.cfm`), // navigate to random page
chromedp.EvaluateAsDevTools(`window.alert = function (txt){return txt}`, &res), // set a function to return the text in the alert box as text
chromedp.EvaluateAsDevTools(`alert('hehe')`, &res), // create an alert box to test the execution
)
if err != nil {
log.Fatal(err)
}
log.Println(res)

参考文章:

https://stackoverflow.com/questions/58085607/chromedp-handle-alert