Golang爬虫终极杀器——Chromedp让你成为二维码登陆终结者(教程)

2023-10-27

Golang爬虫终极杀器——Chromedp让你成为二维码登陆终结者(教程)

  1. Github源码 - chromedp
  2. 文章项目Gitee源码

1 Chromedp是什么

chromedp是一个更快更简单的Golang库用于调用支持Chrome DevTools协议的浏览器,同时不需要额外的依赖(例如SeleniumPhantomJS

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

2 为什么不使用Selenium

对于Golang开发来说,使用chromedp更为便捷,因为它仅仅需要Chrome浏览器而并不需要依赖ChromeDriver,省去了依赖问题,有助于自动化的构建和多平台架构的迁移

3 文章解决了什么需求

  1. 如何使用chromedp进行二维码登陆
  2. 如何将二维码展示在无图形化的终端上(makiuchi-d/gozxing解码 skip2/ go-qrcode编码)
  3. 如何保存Cookies实现短时间免登陆

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

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

4.1 安装chromedp

  1. 下载并安装Chrome浏览器
  2. 创建Golang项目,开启Go Module(在项目目录下使用终端输入go mod init)
  3. 在项目目录下使用终端输入:go get -u github.com/chromedp/chromedp(如果有依赖问题请删除-u

4.2 尝试打开网站(以金山文档https://account.wps.cn/为例)

  1. 重新设置chromedp使用"有头"的方式打开,以便于我们进行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 获取二维码(点击过程)

  1. 需要点击微信登陆按钮,先找到按钮的选择器,右键按钮并在菜单中点击检查,然后可以看到按钮的元素

在这里插入图片描述

  1. 右键元素打开菜单找到copy下的copy selector,即获取到选择器

在这里插入图片描述

  1. 我们尝试点击微信登陆按钮,发现还需要点击一下确认,重复上述步骤获取确认按钮的选择器

在这里插入图片描述

  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. 运行程序即可直达二维码展示界面

在这里插入图片描述

  1. 用同样的方式,获取二维码图片选择器

在这里插入图片描述

  1. 用代码实现获取二维码,有两点需要注意,第一是二维码有加载过程,第二是二维码是元素渲染,我们需要用截图的方式获取(也可以用js来获取对应的href并下载,但是为了照顾小白,选择最简单的)
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. 在上述步骤中,我们已经获取了二维码,接下来我们需要在终端显示二维码,首先是解码,这里使用gozxing
func printQRCode(code []byte) (err error) {
	// 1. 因为我们的字节流是图像,所以我们需要先解码字节流
	img, _, err := image.Decode(bytes.NewReader(code))
	if err != nil {
		return
	}

	// 2. 然后使用gozxing库解码图片获取二进制位图
	bmp, err := gozxing.NewBinaryBitmapFromImage(img)
	if err != nil {
		return
	}

	// 3. 用二进制位图解码获取gozxing的二维码对象
	res, err := qrcode.NewQRCodeReader().Decode(bmp, nil)
	if err != nil {
		return
	}
	return
}
  1. 然后重新编码来输出二维码到终端,这里使用go-qrcode
// 请注意import的库发生了重名
import (
	"github.com/makiuchi-d/gozxing"
	"github.com/makiuchi-d/gozxing/qrcode"
	goQrcode "github.com/skip2/go-qrcode"
)


func printQRCode(code []byte) (err error) {
	// 1. 因为我们的字节流是图像,所以我们需要先解码字节流
	...

	// 2. 然后使用gozxing库解码图片获取二进制位图
	...

	// 3. 用二进制位图解码获取gozxing的二维码对象
	...

	// 4. 用结果来获取go-qrcode对象(注意这里我用了库的别名)
	qr, err := goQrcode.New(res.String(), goQrcode.High)
	if err != nil {
		return
	}

	// 5. 输出到标准输出流
	fmt.Println(qr.ToSmallString(false))

	return
}
  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实现短时间免登陆

  1. 在上述过程中,我们可以通过二维码扫描登陆,网站会在登陆之后进行跳转,跳转后我们需要保存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)
	}
}
  1. 通过上述两步我们已经可以保持登陆状态,然后我们需要检查一下是否成功,这里调用浏览器执行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(),
	}
}
  1. 我们使用已经登陆的cookies运行程序可以发现我们成功跳过登陆过程

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang爬虫终极杀器——Chromedp让你成为二维码登陆终结者(教程) 的相关文章

  • 无法连接到代理“证书由未知机构签名”

    我正在尝试通过 Kubernetes 部署上的 cloudsql proxy 容器连接到 CloudSQL 实例 我已安装 cloudsql 凭据以及值GOOGLE APPLICATION CREDENTIALS set 但是 我的日志中仍
  • Golang 按位运算以及一般字节操作

    我有一些 C 代码 可以对字节执行一些按位运算 我正在尝试在 golang 中做同样的事情 但遇到了困难 C 中的示例 byte a c byte data int j c data j c byte c j c a c 0xFF c 0x
  • 我们如何在 Golang 中组合多个错误字符串?

    我是 golang 新手 我的应用程序需要在循环中返回多个错误 稍后需要组合并作为单个错误字符串返回 我无法使用字符串函数来组合错误消息 在返回之前可以使用什么方法将这些错误合并为一个错误 package main import fmt s
  • Golang 基础知识 struct 和 new() 关键字

    我正在学习 golang 当我阅读描述结构的章节时 我遇到了初始化结构的不同方法 p1 passport var p2 passport p3 passport Photo make byte 0 0 Name Scott Surname
  • nsq 无法通过连接到 nsqlookupd 来消费消息

    我尝试使用 docker compose 来运行 nsq docker compose yml如下 version 3 services nsqlookupd image nsqio nsq command nsqlookupd ports
  • 在函数中将通道作为参数传递的不同方法

    我正在阅读一些Go代码 并说了几种传递Go通道的不同方法 也许它们是相同的 但我想知道是否有任何区别 因为我无法在线找到文档 1 func serve ch lt chan interface do stuff 2 func serve c
  • 如何在C#中执行Go函数

    有没有办法从 C 执行 Go 函数 例如 对于 Python 我会使用 Ironpython 我知道我可以生成一个进程来执行 Go 脚本 但如果可能的话 我真的不想回退到这样的解决方案 Google 搜索没有显示任何内容 那么有什么方法可以
  • 在 Go 中使用电子邮件地址创建证书签名请求 (CSR)

    我尝试使用 crypto x509 包生成 CSR 但没有找到将 emailAddress 字段添加到其主题中的方法 根据文档证书申请 http golang org pkg crypto x509 CertificateRequest结构
  • Golang中如何删除字符串的最后一个字符?

    我想删除字符串的最后一个字符 但在此之前我想检查最后一个字符是否是 如何才能做到这一点 以下是删除尾随加号的几种方法 package main import fmt strings func TrimSuffix s suffix stri
  • 编写每个处理程序中间件

    我希望从处理程序中提取一些重复的逻辑 并将其放入一些每个处理程序的中间件中 特别是 CSRF 检查 检查现有会话值 即身份验证或预览页面 等 我读了关于此的几篇文章 http justinas org writing http middle
  • 递归追加到切片不起作用

    我正在尝试学习 Go 但我不明白为什么递归调用堆栈末尾的这段代码返回一个空切片 有帮助吗 还tmp甚至似乎没有在调试器中注册 func main input 3 int 4 6 7 expected 6 7 4 6 7 4 6 4 7 fm
  • 如何在 Go 中从 stdin 解析无限 json 数组?

    我正在尝试编写一个 i3status 的小替代品 一个与 i3bar 兼容的小程序进行通信this http i3wm org docs i3bar protocol html协议 他们通过标准输入和标准输出交换消息 两个方向的流都是一个无
  • bazel go_embed_data“无法嵌入”

    我将以下 bazel BUILD 配置为 gazelle name gazelle go embed data name static files srcs glob static package main var staticFS go
  • 在 Go 中调用外部命令

    如何在GO中调用外部命令 我需要调用外部程序并等待它完成执行 在执行下一条语句之前 您需要使用执行包 http golang org pkg os exec 使用启动命令Command http golang org pkg os exec
  • 使用ticker定期从经常变化的路径加载内存中的所有文件?

    我有一个应用程序需要从两个不同的路径读取文件 读取所有这些文件后 我需要将它们加载到内存中products map Path Full 这是内存中服务器启动期间需要加载的所有文件的路径 该路径将包含大约 50 个文件 每个文件大小约为 60
  • 指针和切片引用类型 - 接收器

    我认为一旦向我提出这个问题就会有点明显 但现在以下内容还没有点击 type Stack interface func stack Stack Push x interface stack append stack x 我有一个名为 Stac
  • 如何在Postgresql时间戳中存储Golang time.time?

    我可以知道如何存储time timePostgresql 中的对象 例如 SQL 查询 INSERT INTO UserAccount email login time VALUES email protected cdn cgi l em
  • golang 将 big.Float 转换为 big.Int

    将big Float转换为big Int 我在下面编写代码 但它溢出了uint64 那么将big Float转换为big Int的正确方法是什么 package main import fmt import math big func Fl
  • Golang 使用 gomail.v2 实现多个收件人

    我通过 gomail v2 发送电子邮件的唯一方法是在 for 循环中对每封电子邮件使用 Send 函数 但我需要向其他电子邮件地址显示已发送相同的电子邮件 for recipient range os Args 3 mail SetAdd
  • 如何使用 glide golang 安装私有仓库

    我在用glide作为我的 golang 项目的包管理 我无法使用私有存储库glide get bezos gitlab com gomock 即使我提供了正确的凭据 它也会询问用户名和密码 但最终会抛出错误 请提出任何想法 这与glide或

随机推荐

  • 【干货】日志管理与分析(一)——日志收集及来源

    对广大IT工作者 尤其是运维和安全人员来说 日志 是一个再熟悉不过的名词 日志从哪来 机房中的各种软件 系统 防火墙 和硬件 交换机 路由器等 都在不断地生成日志 IT安全业界的无数实践告诉我们 健全的日志记录和分析系统 是系统正常运营 优
  • [C++]抽象工厂模式

    抽象工厂模式 Abstract Factory Pattern 是围绕一个超级工厂创建其他工厂 该超级工厂又称为其他工厂的工厂 这种类型的设计模式属于创建型模式 它提供了一种创建对象的最佳方式 在抽象工厂模式中 接口是负责创建一个相关对象的
  • KETTLE WEB管理控制台设计

    KETTLE WEB管理控制台设计 系统效果图 1 资源配置管理 1 1 用例图 1 2 用例叙述 1 2 1 添加资源库用例 用例名称 添加资源库 前置条件 操作员在启动这个用例之前 必须先执行过 登录 用例 主要事件流 1 当用户选择添
  • 清除input的缓存

    有过表单设计经验的朋友肯定知道 当我们在浏览器中输入表单信息的时候 往往input文本输入框会记录下之前提交表单的信息 以后每次只要双击 input文本输入框就会出现之前输入的文本 这样有时会觉得比较方便 但有时也会暴露用户的隐藏数据 那么
  • oracle简单的备份

    企业之中这种备份肯定用不到 只是闲着没事简单备份一下自己的数据库 方便以后调数据 查阅 一 备份 1 打开cmd 切到oracle的bin目录 2 备份 exp scott 123456 file E oracle备份 scott back
  • CUDA优化:最大化内存吞吐量(官方文档翻译)

    毕业设计要翻译技术资料 3000 字 这里找了英伟达 CUDA TOOLKIT DOCUMENTATION 的 5 3 节 最大化内存吞吐量来 翻译一下 供参考 并希望此文对诸位的 CUDA 程序优化有所帮助 5 3 最大化内存吞吐量 最大
  • 【JPCS 独立出版】2022年机器人与人工智能国际会议(JCRAI 2022)

    2022年机器人与人工智能国际会议 JCRAI 2022 重要信息 会议网址 www jcrai org 会议时间 2022年10月14 16日 召开地点 中国成都 截稿时间 2022年8月30日 录用通知 投稿后2周内 出版社 Journ
  • 【论文&模型讲解】VideoBERT: A Joint Model for Video and Language Representation Learning

    文章目录 前言 0 摘要 1 Introduction 2 相关工作 3 模型 3 1 BERT 3 2 VideoBERT 4 实验与分析 4 1 数据集 4 2 视频和语言预处理 4 3 模型预训练 4 4 zero shot 动作分类
  • Windows与WSL子系统Docker和NVIDIA安装注意事项

    Windows与WSL子系统Docker和NVIDIA安装注意事项 WSL子系统NVIDIA驱动安装的注意事项 确保在windows上安装了CUDA11 8 一定确保CUDA确实安装 一定需要在WSL子系统再重新安装CUDA11 8 设置一
  • windows配置组策略所有题目

    sa禁用cmd 用户配置 策略 管理模板 系统 阻止访问命令提示符 sa隐藏c盘 用户配置 策略 管理员模板 windows组件 文件资源管理器 隐藏我的电脑中的这些指定的驱动 sa组桌面环境 用户配置 管理员模板 桌面 退出时不保存设置
  • python复习之零碎知识点

    马上就要考试了 相信大家在大的知识块上已经都复习的差不多了 今天带大家一起看一看较为细节的一些知识点吧 abs 用来计算复数的模长 python不支持字符与数字相加 gt gt gt a 1 Traceback most recent ca
  • tensorflow1.14.0安装教程--保姆级

    方法不止一种 下面仅展示一种 注 本人电脑为win11 anaconda的python版本为3 9 但tensorflow需要python版本为3 7 所以下面主要阐述将python版本改为3 7后的安装过程以及常遇到的问题 1 首先电脑安
  • GIT 撤销与回滚

    git 相信大家都会用 但是大家有没有遇到过 不小心把不该提交的文件 commit 了 小编整理了一些我在开发中常见的使用场景 供大家参考 场景一 糟了 我刚把不想要的代码 commit到本地仓库中了 但是还没有做push操作 这种场景称为
  • Java什么是对象?

    真实世界对象具有共同的两个特性 它们都有状态和行为 狗有状态 名字 颜色 品种 饥饿 和行为 叫 刨地 摇尾巴 自行车也有状态 当前的齿轮档位 当前的脚踏板频率 当前的速度 和行为 变速 变脚踏板速率 刹车 识别真实世界对象的状态和行为是开
  • ECLIPSE java类文件从实心变为空心,以及右键项目没有 build path,或build path显示 no available source的问题

    导入别人的项目 一开始问题是Java类文件的图标由实心变成了空心 这样的话类文件不能编译 main方法也不能执行 查了半天都说要重新配置 build path 可是我右键项目名根本就没有build path 然后有人说把右上角 javaEE
  • mysql 建表时at line 1_数据库学习笔记之MySQL(11)

    目录 01 三表复合查询 02 对比外键约束的作用 03 添加外键 建表时添加 正文 01 三表复合查询从性能角度讲 多表联合查询没有从单表查询后合并性能高 SELECT a id a name b tel c emailFROM tbl
  • ChatGLM-6B之SSE通信(Server-sent Events)

    目录 研究历程 只是感受 不感兴趣的这段可以跳过 直接看下边的正题 找 正题 二字 正题 1 SSE Server sent Events 2 ChatGLM的流式方法 3 ChatGLM之SSE通信 写这篇博客还是很激动开心的 因为是我经
  • 错误解决——搭建悟空crm9项目运行环境过程中遇到的一些问题和错误解决办法

    错误解决 搭建悟空crm9项目运行环境过程中遇到的一些问题和错误解决办法 一 安装jdk 二 安装Tomcat 问题 1 三 安装MySQL 四 安装Redis 问题 1 问题 2 问题 3 五 安装悟空crm9 问题 1 问题 2 问题
  • C# 静态类和sealed类(密封类)的区别

    网上看到很多文章写静态类 和密封类 但是鲜有它们的对比总结 在此简单总结一下 静态类 Static Class 静态类不能被实例化 其成员都是静态的 可以通过类名直接访问 静态类通常用于创建一组相关的静态方法和属性 提供一些全局功能或工具函
  • Golang爬虫终极杀器——Chromedp让你成为二维码登陆终结者(教程)

    Golang爬虫终极杀器 Chromedp让你成为二维码登陆终结者 教程 Github源码 chromedp 文章项目Gitee源码 1 Chromedp是什么 chromedp是一个更快 更简单的Golang库用于调用支持Chrome D