Golang爬虫终极杀器——Chromedp让你成为二维码登陆终结者(教程) 1 Chromedp是什么
chromedp更快更简单Chrome DevTools协议SeleniumPhantomJS

Chrome和Golang都与Google有着相当密切的关系,而Chrome DevTools其实就是Chrome浏览器按下F12之后的控制终端

2 为什么不使用Selenium
GolangchromedpChromeChromeDriver
3 文章解决了什么需求

网站会更新,文章不保证更新,请务必学会举一反三

4 如何使用chromedp进行二维码登陆

4.1 安装chromedp

Go Modulego mod initgo get -u github.com/chromedp/chromedp-u
https://account.wps.cn/
"有头"debug
func main(){
    // chromdp依赖context上限传递参数
	ctx, _ := chromedp.NewExecAllocator(
		context.Background(),

		// 以默认配置的数组为基础,覆写headless参数
		// 当然也可以根据自己的需要进行修改,这个flag是浏览器的设置
		append(
			chromedp.DefaultExecAllocatorOptions[:],
			chromedp.Flag("headless", false),
		)...,
	)
}
  1. 创建chromedp上下文对象
func main(){
    // chromdp依赖context上限传递参数
    ...
    
    // 创建新的chromedp上下文对象,超时时间的设置不分先后
    // 注意第二个返回的参数是cancel(),只是我省略了
	ctx, _ = context.WithTimeout(ctx, 30*time.Second)
	ctx, _ = chromedp.NewContext(
		ctx,
		// 设置日志方法
		chromedp.WithLogf(log.Printf),
	)

	// 通常可以使用 defer cancel() 去取消
	// 但是在Windows环境下,我们希望程序能顺带关闭掉浏览器
	// 如果不希望浏览器关闭,使用cancel()方法即可
	// defer cancel()
	// defer chromedp.Cancel(ctx)
}
  1. 执行自定义的任务
func main(){
    // chromdp依赖context上限传递参数
    ...
    
    // 创建新的chromedp上下文对象,超时时间的设置不分先后
    // 注意第二个返回的参数是cancel(),只是我省略了
    ...
    
    // 执行我们自定义的任务 - myTasks函数在第4步
	if err := chromedp.Run(ctx, myTasks()); err != nil {
		log.Fatal(err)
		return
	}
}
  1. 至此程序的初始化过程已经完成,接下来就是任务——打开登陆页面
// 自定义任务
func myTasks() chromedp.Tasks {
	return chromedp.Tasks{
		// 1. 打开金山文档的登陆界面
		chromedp.Navigate(loginURL),
	}
}
  1. 运行一下程序,可以看到Chrome被打开,同时访问了我们指定的网站

在这里插入图片描述

4.3 获取二维码(点击过程)

微信登陆选择器检查

在这里插入图片描述

copycopy selector选择器

在这里插入图片描述

确认按钮

在这里插入图片描述

  1. 用代码执行上述点击步骤
// 自定义任务
func myTasks() chromedp.Tasks {
	return chromedp.Tasks{
		// 1. 打开金山文档的登陆界面
		chromedp.Navigate(loginURL),

		// 2. 点击微信登陆按钮
		// #wechat > span:nth-child(2)
		chromedp.Click(`#wechat > span:nth-child(2)`),

		// 3. 点击确认按钮
		// #dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok
		chromedp.Click(`#dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok`),
	}
}
  1. 运行程序即可直达二维码展示界面

在这里插入图片描述

二维码图片选择器

在这里插入图片描述

jshref
func myTasks() chromedp.Tasks {
	return chromedp.Tasks{
		// 1. 打开金山文档的登陆界面
		...

		// 2. 点击微信登陆按钮
		...

		// 3. 点击确认按钮
		...

		// 4. 获取二维码
		// #wximport
		getCode(),
	}
}

func getCode() chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		// 1. 用于存储图片的字节切片
		var code []byte

		// 2. 截图
		// 注意这里需要注明直接使用ID选择器来获取元素(chromedp.ByID)
		if err = chromedp.Screenshot(`#wximport`, &code, chromedp.ByID).Do(ctx); err != nil {
			return
		}

		// 3. 保存文件
		if err = ioutil.WriteFile("code.png", code, 0755); err != nil {
			return
		}
		return
	}
}
  1. 执行程序即可发现目录下已经存储了二维码图片文件,我们可以通过扫描此二维码进行登陆,与浏览器上扫描为同一种效果

在这里插入图片描述

5. 如何将二维码展示在无图形化的终端上(与chromedp无关,属于额外内容)

  1. 修改我们第二步的过程
func getCode() chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		// 1. 用于存储图片的字节切片
		...

		// 2. 截图
		// 注意这里需要注明直接使用ID选择器来获取元素(chromedp.ByID)
		...

		// 3. 把二维码输出到标准输出流
		if err = printQRCode(code); err != nil {
			return err
		}
		return
	}
}
  1. 运行程序即可查看效果

在这里插入图片描述

6. 如何保存Cookies实现短时间免登陆
cookies
// 保存Cookies
func saveCookies() chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		// 等待二维码登陆
		if err = chromedp.WaitVisible(`#app`, chromedp.ByID).Do(ctx); err != nil {
			return
		}

		// cookies的获取对应是在devTools的network面板中
		// 1. 获取cookies
		cookies, err := network.GetAllCookies().Do(ctx)
		if err != nil {
			return
		}

		// 2. 序列化
		cookiesData, err := network.GetAllCookiesReturns{Cookies: cookies}.MarshalJSON()
		if err != nil {
			return
		}

		// 3. 存储到临时文件
		if err = ioutil.WriteFile("cookies.tmp", cookiesData, 0755); err != nil {
			return
		}
		return
	}
}
  1. 获取到Cookies之后,我们需要在程序运行时将Cookies从临时文件中加载到浏览器中
// 加载Cookies
func loadCookies() chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		// 如果cookies临时文件不存在则直接跳过
		if _, _err := os.Stat("cookies.tmp"); os.IsNotExist(_err) {
			return
		}

		// 如果存在则读取cookies的数据
		cookiesData, err := ioutil.ReadFile("cookies.tmp")
		if err != nil {
			return
		}

		// 反序列化
		cookiesParams := network.SetCookiesParams{}
		if err = cookiesParams.UnmarshalJSON(cookiesData); err != nil {
			return
		}

		// 设置cookies
		return network.SetCookies(cookiesParams.Cookies).Do(ctx)
	}
}
js
// 检查是否登陆
func checkLoginStatus() chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		var url string
		if err = chromedp.Evaluate(`window.location.href`, &url).Do(ctx); err != nil {
			return
		}
		if strings.Contains(url, "https://account.wps.cn/usercenter/apps") {
			log.Println("已经使用cookies登陆")
			chromedp.Stop()
		}
		return
	}
}
  1. 最终重新设置我们的浏览器任务即可
// 自定义任务
func myTasks() chromedp.Tasks {
	return chromedp.Tasks{
		// 0. 加载cookies <-- 变动
		loadCookies(),

		// 1. 打开金山文档的登陆界面
		...

		// 判断一下是否已经登陆  <-- 变动
		checkLoginStatus(),

		// 2. 点击微信登陆按钮
		// #wechat > span:nth-child(2)
		...

		// 3. 点击确认按钮
		// #dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok
		...

		// 4. 获取二维码
		// #wximport
		...

		// 5. 若二维码登录后,浏览器会自动跳转到用户信息页面  <-- 变动
		saveCookies(),
	}
}
已经登陆的cookies

在这里插入图片描述