原文:https://lwebapp.com/zh/post/bypass-captcha

需求

日常工作当中,为了提高工作效率,我们可能会写脚本来自动执行任务。有些网站因为需要用户登陆,所以脚本的自动登陆功能必不可少。

不过我们在登陆网站的时候经常会出现验证码,验证码的目的就是为了防止机器登陆、自动化脚本操作,那么有没有办法让脚本能自动识别验证码实现登陆呢?

接下来我以 B 站为例给大家讲解下,如何解决自动登陆脚本中最关键的验证码问题。

探索

首先需要体验下这个网站的登陆方式,了解下它的验证码类型。

打开 B 站 https://www.bilibili.com/ ,打开控制台,点击登陆,这时候会弹出中间小的登陆框,通常输入完账号和密码,就会弹出验证码框了,我们猜测验证码的接口此时已经请求了。

由于验证码的英文是 captcha ,我们在 network 面板里搜 captcha

发现了一个和验证码相关的接口

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 ,也可以参照这个教程,解决问题的思路都是一样的。

解决

  1. 如何识别验证码

先仔细阅读官方文档 2Captcha API Geetest,解决方案写的很详细,简单来说

  • 通过拦截网站接口,获取gtchallenge这两个校验码参数,请求http://2captcha.com/in.php,得到验证码 ID
  • 隔一段时间再请求http://2captcha.com/res.php,得到校验成功的标识 challengevalidateseccode
  1. 如何应用验证结果

拿到最关键的 validate 之后,模拟用户填写账号密码登陆,拦截验证码请求接口的返回参数,替换为校验成功的参数,随即触发登陆接口。

接下来,我们分析下详细步骤

环境准备

我们先搭建一下脚本执行环境。

我们使用 Node.js + Playwright 来写脚本。

  1. 先确保你的电脑本地已经安装了 Nodejs

  2. 再新建空项目,安装 Playwright

mkdir bypass-captcha
cd bypass-captcha
npm init
npm i -D playwright

我们采用 Playwright 的库模式,详细文档:Playwright

  1. 在项目根目录新建一个脚本文件 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 接口

  1. 首先整理下请求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
  • gtchallenge 之前已经在网站登录页面的接口中看到了。不过这里有个注意点,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&gt=ac597a4506fee079629df5d8b66dd4fe&challenge=12345678abc90123d45678ef90123a456b&pageurl=https://www.bilibili.com/
  1. 接下来就解决每次进首页获取新的 challenge

模拟用户点击登录的流程

  • 先启动谷歌浏览器,打开 B 站首页

  • 点击顶部的登录按钮,会弹出登录框

  • 这时候验证码接口已经发出,在这里监听验证码接口返回的参数,就能截获到gtchallenge的值

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);
}
  1. 使用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 写在环境变量文件中来引用。

  1. 在根目录新建一个环境变量文件.env,写入API Key的值
# .env文件
API_KEY="d34y92u74en96yu6530t5p2i2oe3oqy9"
  1. 然后安装dotenv库,用来获取到环境变量
npm i dotenv
  1. 在 js 脚本中使用
require("dotenv").config();

这样通过 process.env.API_KEY 就能取到 .env 中的变量了,通常 .env 文件不上传到代码仓库,保证个人信息的安全性。

  1. 如果不想把信息写到文件中的同时确保安全性,也可以直接在控制台输入传入 Node.js 环境变量,比如
API_KEY=d34y92u74en96yu6530t5p2i2oe3oqy9 node captcha.js

请求 res.php 接口

  1. 请求接口之前,我们也整理下所需参数
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
  1. 上一个请求 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);}}}
);

接口会返回三个值 challengevalidateseccode ,每一个参数都是一个字符串

{"geetest_challenge": "aeb4653fb336f5dcd63baecb0d51a1f3","geetest_validate": "9f36e8f3a928a7d382dad8f6c1b10429","geetest_seccode": "9f36e8f3a928a7d382dad8f6c1b10429|jordan"
}

其中 challenge 就是前面我们拦截到的参数, validate 是校验结果标识, seccode 内容和 validate 基本一致,只多了一个单词。我们需要将 validate 存储下来备用。

这里有时候会碰到验证码无法校验通过的情况,可以多尝试几次,或者联系 2Captcha 官网排查问题

到这里,验证码校验结果的信息已经获取完整,接下来就是拿校验结果去登陆了。

登陆

  1. 先来研究下正常用户点击验证码校验成功后的登陆流程

我们发现了三个接口

  • https://api.geetest.com/ajax.php:验证码接口,用于生成验证码和校验验证码是否通过。校验接口返回数据中的 validate 字段就是 2Captcha 服务获取到的 geetest_validate

  • https://passport.bilibili.com/x/passport-login/web/key?_=1649087831803:密码加密接口,用于获取 hash 和公钥

  • https://passport.bilibili.com/x/passport-login/web/login:登陆接口,入参包括账号、密码、tokenchallengevalidateseccode

我们分析这几个接口,可以得出两个登陆方案。

  1. 第一个方案,在Node.js 环境下请求加密接口和登陆接口,获取用户 Cookie 信息,后续用户登陆直接携带 Cookie 信息即可。这个方案的难点是需要单独处理密码加密,对新手不太友好。
  2. 第二个方案,用 Playwright 模拟用户填写账号密码登陆,随机点击验证码触发登陆,拦截验证码接口的返回参数,替换为校验成功的标识,随即触发登陆接口。

我们采用第二种方案。

不过也遇到一个坑,就是 Node.js 环境下,验证码图片加载不出来,后面发现验证码接口 https://api.geetest.com/ajax.php 同时负责拉取验证码图片和校验验证码,我们直接拦截拉取验证码图片时的请求,替换校验结果就能触发登陆了,不需要等图片验证码出来。这个细节很关键。

总结

以上就是针对自动化测试任务中,常见的自动登陆功能的一些研究。结合 Node.jsPlaywright2Captcha 这些工具的优势,实现了验证码识别。完整的代码我已经上传到了 GitHub。

仓库:https://github.com/openHacking/bypass-captcha

原文:https://lwebapp.com/zh/post/bypass-captcha

可能还有很多待优化的地方,欢迎大家指出。

声明:此脚本只作为测试和学习的案例,风险自行评估。

参考

  • Nodejs Playwright 2Captcha 验证码识别实现自动登陆
  • Playwright
  • 2Captcha
  • Python 自动登录哔哩哔哩(2captcha 打码平台)

Nodejs Playwright 2Captcha 验证码识别实现自动登陆相关推荐

  1. Python3调用图灵验证码 95%成功率识别网易易盾中文点选验证码 网易邮箱自动登陆 (非人工打码)

    最近遇到一个问题,需要频繁切换账号登陆网易邮箱,但是需要识别网易的中文验证码,比较麻烦.而且因为需要24h不间断的操作,所以没法使用人工打码平台,而且打码平台感觉也很贵--延迟又高.最后找到了一个可以 ...

  2. 使用Python+Selenium+图灵验证码识别平台,识别B站/bilibili的中文验证码,并自动登陆B站

    一直想用python写一个程序帮我自动登陆B站,完成一些点击任务,懂的都懂 =v= 最近终于腾出时间来搞了,其实最难的部分就是中文验证码的识别.这个借助API接口也能轻松搞定.下面分享一下全部源码(前 ...

  3. python+selenium+pytesseract实现自动识别简单验证码,并且自动登陆网站

    文章目录 功能介绍 首先安装需要的库 编写代码 功能介绍 本文章实现自动识别验证码并且自动登陆网站,完全不需要人工操作 Python版本:3.6 Selenium:selenium 是一个用于 Web ...

  4. 用爬虫实现验证码识别并模拟登陆和cookie操作、代理操作、线程池

    一.模拟登陆 1.为什么要进行模拟登陆 有时,我们需要爬取一些基于个人用户的用户信息(需要登陆后才可以查看) 2.为什么要需要识别验证码 因为验证码往往是作为登陆请求中的请求参数被使用 3.验证码识别 ...

  5. python脚本根据cookies自动登录网站_python实现带验证码网站的自动登陆实现代码...

    早听说用python做网络爬虫非常方便,正好这几天单位也有这样的需求,需要登陆XX网站下载部分文档,于是自己亲身试验了一番,效果还不错. 本例所登录的某网站需要提供用户名,密码和验证码,在此使用了py ...

  6. 使用Jsp+Servlet的wlop官网(验证码登录+session自动登陆)

    这个页面是我学习前端的时候自己使用JQuery写的轮播图,登录功能的后端判断未实现,现将登陆功能补充完整. 需求: 1,验证码登录# 2,账号密码登录# 3,登录失败在登录部分提示相应的信息# 3,登 ...

  7. python图像验证码识别_python 简单图像识别--验证码

    python  简单图像识别--验证码 记录下,准备工作安装过程很是麻烦. 首先库:pytesseract,image,tesseract,PIL windows安装PIL,直接exe进行安装更方便( ...

  8. 图片验证码识别程序全面分析

    我们在登陆账号的时候,发现有图片验证码需要识别,我们在注册账号的时候,也会有图片验证码需要识别.很多"程序猿"可能也在找如何进行图片验证码识别的编程.图片验证码常有,但是关于如何快 ...

  9. Python模拟登录,Python识别图形验证码实现自动登陆

    前言 利用Python识别图形验证码,selenium模块实现自动登陆.废话不多说. 让我们愉快地开始吧~ 开发工具 Python版本: 3.6.4 相关模块: re: numpy模块: pytess ...

最新文章

  1. “〜”(波浪号/波浪形/旋转)CSS选择器是什么意思?
  2. 【hiho】38 二分·二分答案【二分答案】
  3. nginx: [emerg] socket() [::]:80 failed (97: Address family not supported by protocol)
  4. 注释嵌套注释_注释,无处不在的注释
  5. Windows、Linux 纷纷被爆漏洞,黑客可直取 root 权限!
  6. html加速度陀螺仪坐标,如何从Javascript访问加速度计/陀螺仪数据?
  7. msysgit中文问题
  8. 大数据开发之CDH篇----cloudera-scm-agent启动不了后的一堆事
  9. 超级好用的高颜值终端工具---Tabby
  10. 成功解决python.exe 无法找到入口 无法定位程序输入点
  11. scheduling jobs未起来
  12. 无人驾驶感知篇之传感器标定(二)
  13. 什么是枚举,及枚举的特点
  14. WatchOS开发教程之四: Watch与 iPhone的通信和数据共享
  15. wordpress新留言微信提醒
  16. 如何将PPT进行压缩?简单的方法介绍
  17. Fiddler跟F12
  18. 天才程序员: 那些年我偷懒没敲的EOS代码, 让我失去了一切, 如果...
  19. 2021.11.11
  20. Pytorch框架TorchScript模型转换方法

热门文章

  1. Node.js中multer的相关操作
  2. js实现数据的excel下载
  3. c语言前缀编码,C语言实现中缀表达式转前缀表达式
  4. 测验2 PYTHON基本图形的绘制
  5. 剑指Offer——小米+小红书笔试题+知识点总结
  6. web端测试和移动端测试的对比
  7. mysql的delete语句_mysql删除语句
  8. java实现蛇蛇大作战_蛇蛇大作战扩大视角
  9. Spark大数据分析实战-公司销售数据分析
  10. php Wrapper LFI,透過 LFI 引入 PHP session 檔案觸發 RCE