Nodejs Playwright 自动识别验证码登陆B站
原文:https://lwebapp.com/zh/post/bypass-captcha
需求
日常工作当中,为了提高工作效率,我们可能会写脚本来自动执行任务。有些网站因为需要用户登陆,所以脚本的自动登陆功能必不可少。
不过我们在登陆网站的时候经常会出现验证码,验证码的目的就是为了防止机器登陆、自动化脚本操作,那么有没有办法让脚本能自动识别验证码实现登陆呢?
接下来我以 B 站为例给大家讲解下,如何解决自动登陆脚本中最关键的验证码问题。
探索
首先需要体验下这个网站的登陆方式,了解下它的验证码类型。
打开 B 站 https://www.bilibili.com/ ,打开控制台,点击登陆,这时候会弹出中间小的登陆框,通常输入完账号和密码,就会弹出验证码框了,我们猜测验证码的接口此时已经请求了。
由于验证码的英文是 captcha
,我们在 network
面板里搜 captcha
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cv3sLJ37-1649653499343)(https://cdn.jsdelivr.net/gh/openHacking/static-files@main/uPic/uTools_1649263456820.png)]
发现了一个和验证码相关的接口
https://passport.bilibili.com/x/passport-login/captcha
点开看接口返回结果,果然有一些有用的信息,我们发现验证码类型是 geetest
。
{"code": 0,"message": "0","ttl": 1,"data": {"type": "geetest","token": "b416c387953540608bb5da384b4e372b","geetest": {"challenge": "aeb4653fb336f5dcd63baecb0d51a1f3","gt": "ac597a4506fee079629df5d8b66dd4fe"},"tencent": {"appid": ""}}
}
通过搜索发现了 B 站使用的验证码服务是 geetest
提供的,国内有很多网站都用的这个服务, geetest
验证码的特点是移动拼图、按顺序选择文字或者数字。
那么接下来,就来寻找可以识别 geetest
验证码的办法。
小编了解了市面上提供的验证码解决方案,效果比较好的基本都是 OCR 服务商,对比之后发现 2Captcha 的服务非常好,解码速度快、服务器连接稳定、支持多种语言 API、价格公道,小编决定试用下 2Captcha
。
2Captcha 官网
接下来就展示使用 Nodejs
+ Playwright
+ 2Captcha
来解决 B 站的登陆验证码问题。
如果想用其他语言和框架,比如
Python
+Selenium
,也可以参照这个教程,解决问题的思路都是一样的。
解决
- 如何识别验证码
先仔细阅读官方文档 2Captcha API Geetest,解决方案写的很详细,简单来说
- 通过拦截网站接口,获取
gt
、challenge
这两个校验码参数,请求http://2captcha.com/in.php
,得到验证码ID
- 隔一段时间再请求
http://2captcha.com/res.php
,得到校验成功的标识challenge
、validate
、seccode
- 如何应用验证结果
拿到最关键的 validate
之后,模拟用户填写账号密码登陆,拦截验证码请求接口的返回参数,替换为校验成功的参数,随即触发登陆接口。
接下来,我们分析下详细步骤
环境准备
我们先搭建一下脚本执行环境。
我们使用 Node.js
+ Playwright
来写脚本。
先确保你的电脑本地已经安装了 Nodejs
再新建空项目,安装
Playwright
mkdir bypass-captcha
cd bypass-captcha
npm init
npm i -D playwright
我们采用
Playwright
的库模式,详细文档:Playwright
- 在项目根目录新建一个脚本文件
captcha.js
,填入以下内容,命令行运行node captcha.js
来简单测试下是否能正常启动项目
const {chromium
} = require("playwright");(async () => {const browser = await chromium.launch({headless: false,});const page = await browser.newPage();await page.goto("https://www.bilibili.com/");await browser.close();
})();
正常情况下会弹出一个谷歌浏览器界面,显示 B 站首页,随后浏览器自动关闭。
请求 in.php
接口
- 首先整理下请求
http://2captcha.com/in.php
接口所需要的参数有哪些,可以看下参数列表,我们关注下必传参数
参数 | 类型 | 必选 | 描述 |
---|---|---|---|
key | String | 是 | 您的 API key |
method | String | 是 | geetest - 定义您正在发送的是 Geetest 的验证码 |
gt | String | 是 | 在目标网站上找到的 gt 参数 |
challenge | String | 是 | 在目标网站上找到的 challenge 参数 |
api_server | String | 否 | 在目标网站上找到的 api_server 参数 |
pageurl | String | 是 | 您看到 Geetest 验证码时所在网页的完整 URL |
header_acao | Integer 默认: 0 | 否 |
0 - 禁用 1 - 启用。 如果启用 in.php 将在响应中包含 Access-Control-Allow-Origin:* 标头。 用于 Web 应用程序中的跨域 AJAX 请求。 res.php 也支持
|
pingback | String | 否 | 解决验证码时将发送的 pingback(回调)响应的 URL。 URL 应该在服务器上注册。 更多信息在这里 |
json | Integer 默认: 0 | 否 | 0 - 服务器将以纯文本形式发送响应 1 - 告诉服务器以 JSON 格式发送响应 |
soft_id | Integer | 否 | 软件开发人员的 ID。 将他们的软件与 2Captcha 集成的开发人员将获得奖励:软件用户支出的 10%。 |
proxy | String | 否 | 格式: login:password@123.123.123.123:3128 您可以找到更多关于代理的信息这里。 |
proxytype | String | 否 | 代理类型: HTTP, HTTPS, SOCKS4, SOCKS5. |
userAgent | String | 否 | 您的 userAgent 将传递给我们的工作人员并用于解决验证码。 |
key
是需要在 2Captcha 官网注册账户后,后台面板的账户设置中就有一个API key
,当然要起让 key 生效的话还需要充值一定金额method
是一个固定值geetest
gt
和challenge
之前已经在网站登录页面的接口中看到了。不过这里有个注意点,gt
是每个网站只有一个值,B 站这里是固定的ac597a4506fee079629df5d8b66dd4fe
,但是challenge
是一个动态值,每次 API 请求都会获得一个新的challenge
值。在页面上加载验证码后,challenge
值就会失效。 所以要在网站登录页加载的时候监听https://passport.bilibili.com/x/passport-login/captcha
这个请求,每次重新识别出新的challenge
值。下面会讲解如何监听到。pageurl
就是登录页的地址https://www.bilibili.com/
于是我们可以得到类似这样一个请求接口
http://2captcha.com/in.php?key=1abc234de56fab7c89012d34e56fa7b8&method=geetest>=ac597a4506fee079629df5d8b66dd4fe&challenge=12345678abc90123d45678ef90123a456b&pageurl=https://www.bilibili.com/
- 接下来就解决每次进首页获取新的
challenge
值
模拟用户点击登录的流程
先启动谷歌浏览器,打开 B 站首页
点击顶部的登录按钮,会弹出登录框
这时候验证码接口已经发出,在这里监听验证码接口返回的参数,就能截获到
gt
和challenge
的值
const {chromium
} = require("playwright");(async () => {// 选择Chrome浏览器,设置headless: false 能看到浏览器界面const browser = await chromium.launch({headless: false,});const page = await browser.newPage();// 打开B站await page.goto("https://www.bilibili.com/");const [response] = await Promise.all([// 请求验证码接口page.waitForResponse((response) =>response.url().includes("/x/passport-login/captcha") &&response.status() === 200),// 点击顶部的登录按钮page.click(".header-login-entry"),]);// 获取到接口返回信息const responseJson = await response.body();// 解析出 gt 和 challengeconst json = JSON.parse(responseJson);const gt = json.data.geetest.gt;const challenge = json.data.geetest.challenge;console.log("得到 gt", gt, "challenge", challenge);// 暂停5秒,防止浏览器关闭太快,来不及看到效果sleep(5000);// 关闭浏览器await browser.close();
})();/*** 模拟sleep功能,延迟一定时间,单位毫秒* Delay for a number of milliseconds*/
function sleep(delay) {var start = new Date().getTime();while (new Date().getTime() < start + delay);
}
- 使用
request
库来单独请求in.php
接口
先安装 request
npm i request
现在可以开始正式的请求 http://2captcha.com/in.php
接口了
// 请求 in.php 接口
const inData = {key: API_KEY,method: METHOD,gt: gt,challenge: challenge,pageurl: PAGE_URL,json: 1,
};request.post("http://2captcha.com/in.php", {json: inData},function(error, response, body) {if (!error && response.statusCode == 200) {console.log("response", body);}}
);
正常情况下,这时候会返回验证码 ID
,比如 {"status":1,"request":"2122988149"}
,取 request
字段即可。
如果接口返回代码
ERROR_ZERO_BALANCE
,表明您的账户余额不足,需要充值,我这里充值了最低额度用于演示,大家根据自己需要适当体验下。
扩展学习
为了提升安全性,我们将 API Key
写在环境变量文件中来引用。
- 在根目录新建一个环境变量文件
.env
,写入API Key
的值
# .env文件
API_KEY="d34y92u74en96yu6530t5p2i2oe3oqy9"
- 然后安装
dotenv
库,用来获取到环境变量
npm i dotenv
- 在 js 脚本中使用
require("dotenv").config();
这样通过 process.env.API_KEY
就能取到 .env
中的变量了,通常 .env
文件不上传到代码仓库,保证个人信息的安全性。
- 如果不想把信息写到文件中的同时确保安全性,也可以直接在控制台输入传入 Node.js 环境变量,比如
API_KEY=d34y92u74en96yu6530t5p2i2oe3oqy9 node captcha.js
请求 res.php
接口
- 请求接口之前,我们也整理下所需参数
GET 参数 | 类型 | 必选 | 描述 |
---|---|---|---|
GET 参数 | 类型 | 必选 | 描述 |
key | String | 是 | 您的 API key |
action | String | 是 |
get - 得到您的验证码的答案
|
id | Integer | 是 | in.php 返回的验证码 ID |
json | Integer 默认: 1 | 否 | 服务器将始终以 JSON 格式返回 Geetest 验证码的响应。 |
key
就是API_KEY
,上一个接口也用到了action
就是固定值get
id
是刚刚in.php
返回的验证码ID
- 上一个请求 20 秒之后,再请求
http://2captcha.com/res.php
接口获取验证结果
request.get(`http://2captcha.com/res.php?key=${API_KEY}&action=get&id=${ID}&json=1`,function(error, response, body) {if (!error && response.statusCode == 200) {const data = JSON.parse(body);if (data.status == 1) {console.log(data.request);}}}
);
接口会返回三个值 challenge
、 validate
和 seccode
,每一个参数都是一个字符串
{"geetest_challenge": "aeb4653fb336f5dcd63baecb0d51a1f3","geetest_validate": "9f36e8f3a928a7d382dad8f6c1b10429","geetest_seccode": "9f36e8f3a928a7d382dad8f6c1b10429|jordan"
}
其中 challenge
就是前面我们拦截到的参数, validate
是校验结果标识, seccode
内容和 validate
基本一致,只多了一个单词。我们需要将 validate
存储下来备用。
这里有时候会碰到验证码无法校验通过的情况,可以多尝试几次,或者联系 2Captcha 官网排查问题
到这里,验证码校验结果的信息已经获取完整,接下来就是拿校验结果去登陆了。
登陆
- 先来研究下正常用户点击验证码校验成功后的登陆流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TrJUkZBL-1649653499344)(https://cdn.jsdelivr.net/gh/openHacking/static-files@main/uPic/uTools_1649168958947.png)]
我们发现了三个接口
https://api.geetest.com/ajax.php
:验证码接口,用于生成验证码和校验验证码是否通过。校验接口返回数据中的validate
字段就是 2Captcha 服务获取到的geetest_validate
。
https://passport.bilibili.com/x/passport-login/web/key?_=1649087831803
:密码加密接口,用于获取 hash 和公钥
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fuG5XEPX-1649653499346)(https://cdn.jsdelivr.net/gh/openHacking/static-files@main/uPic/nGQQKe.png)]https://passport.bilibili.com/x/passport-login/web/login
:登陆接口,入参包括账号、密码、token
、challenge
、validate
和seccode
等
我们分析这几个接口,可以得出两个登陆方案。
- 第一个方案,在
Node.js
环境下请求加密接口和登陆接口,获取用户 Cookie 信息,后续用户登陆直接携带 Cookie 信息即可。这个方案的难点是需要单独处理密码加密,对新手不太友好。 - 第二个方案,用
Playwright
模拟用户填写账号密码登陆,随机点击验证码触发登陆,拦截验证码接口的返回参数,替换为校验成功的标识,随即触发登陆接口。
我们采用第二种方案。
不过也遇到一个坑,就是 Node.js
环境下,验证码图片加载不出来,后面发现验证码接口 https://api.geetest.com/ajax.php
同时负责拉取验证码图片和校验验证码,我们直接拦截拉取验证码图片时的请求,替换校验结果就能触发登陆了,不需要等图片验证码出来。这个细节很关键。
总结
以上就是针对自动化测试任务中,常见的自动登陆功能的一些研究。结合 Node.js
、 Playwright
、 2Captcha
这些工具的优势,实现了验证码识别。完整的代码我已经上传到了 GitHub。
仓库:https://github.com/openHacking/bypass-captcha
原文:https://lwebapp.com/zh/post/bypass-captcha
可能还有很多待优化的地方,欢迎大家指出。
声明:此脚本只作为测试和学习的案例,风险自行评估。
参考
- Nodejs Playwright 2Captcha 验证码识别实现自动登陆
- Playwright
- 2Captcha
- Python 自动登录哔哩哔哩(2captcha 打码平台)
Nodejs Playwright 自动识别验证码登陆B站相关推荐
- Nodejs Playwright 2Captcha 验证码识别实现自动登陆
原文:https://lwebapp.com/zh/post/bypass-captcha 需求 日常工作当中,为了提高工作效率,我们可能会写脚本来自动执行任务.有些网站因为需要用户登陆,所以脚本的自 ...
- python tesseract selenium自动识别验证码登陆
文章目录 前言 一.下载安装tesserocr 1. 环境:win7 x64位 +python3.6 2. 下载链接 3.安装注意事项 二.安装pytesseract和Pillow包 1.使用pych ...
- Python爬虫自动识别验证码登陆
目录 一.思路 二.代码 三.注意事项 一.思路 使用selenium先get到页面,截取登陆界面(包括验证码).代码处理+ocr识别,自从输出验证码,再使用selenium定位到登陆元素,点击登陆即 ...
- 使用Python+Selenium+图灵验证码识别平台,识别B站/bilibili的中文验证码,并自动登陆B站
一直想用python写一个程序帮我自动登陆B站,完成一些点击任务,懂的都懂 =v= 最近终于腾出时间来搞了,其实最难的部分就是中文验证码的识别.这个借助API接口也能轻松搞定.下面分享一下全部源码(前 ...
- python中selenium(模拟登陆)+pytesseract(自动识别验证码)应用例子之查询住房公积金
这里以查询深圳住房公积金为例(http://gjj.sz.gov.cn/fzgn/zfcq/) from selenium import webdriver # 模拟浏览器操作 from PIL im ...
- 2021最新 python爬取12306列车信息自动抢票并自动识别验证码(三)购票篇
项目前言 tiebanggg又来更新了,项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除:切忌用于一切非法途径,否则后果自行 ...
- 2021最新python爬取12306列车信息自动抢票并自动识别验证码
项目描述 项目前言 tiebanggg又来更新了,项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除:切忌用于一切非法途径,否 ...
- python爬取12306列车信息自动抢票并自动识别验证码(一)列车数据获取篇
项目前言 自学python差不多有一年半载了,这两天利用在甲方公司搬砖空闲之余写了个小项目--[12306-tiebanggg-master].注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一 ...
- python爬取12306列车信息自动抢票并自动识别验证码(二)selenium登录验证篇
项目前言 自学python差不多有一年半载了,这两天利用在甲方公司搬砖空闲之余写了个小项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时 ...
- 使用selenium自动登陆b站 图片文字验证识别
文章目录 前言 一. 反,反反爬虫 1.反爬虫 2.反反爬虫 二,超级鹰 三.完整代码 1.导包 2.超级鹰接口 3.连接手动开启的浏览器 4.定位文本框标签 5.图片文字识别 6.文本处理,坐标处理 ...
最新文章
- andriod studio 注释乱码问题
- break continue
- sharp扫地机器人讲话_扫地机机器人,智能扫地机器人推荐
- 数据中心水冷系统一次泵与二次泵的选择
- 昨天日志 今天日志的获取方法
- 【专升本计算机】甘肃省专升本计算机C语言经典程序案例代码合集(持续更新)
- 第二十七期:Deepfake视频正在快速传播,也许区块链能够阻止这波“瘟疫”
- 电闹上赠么使用计算机,计算器怎么算n次方 怎么用电脑计算器算次方
- C#获取cpu序列号 硬盘ID 网卡硬地址以及操作注册表 .
- 支付顺序--微信支付到公司账户--待出票
- web前端期末大作业 html+css+javascript防天天生鲜官网网页设计实例 企业网站制作
- rm -rf —— 删库跑路教程详解
- 单链表的逆置(递归和非递归)
- 【SAMMY】DOS下操作隐藏文件、文件夹
- Excel导入导出功能
- 综合日语第一册第八课
- Django Rest_framework 1(RESTRESTful)
- 兆骑科创创新创业服务平台,海内外高层次人才引进,赛事承办
- C语言案例之走迷宫(Ubuntu)
- 加强杂交和环境选择的高维目标进化算法