关于javascript:Nodejs-Playwright-2Captcha-验证码识别实现自动登陆

45次阅读

共计 6772 个字符,预计需要花费 17 分钟才能阅读完成。

原文:https://lwebapp.com/zh/post/b…

需要

日常工作当中,为了进步工作效率,咱们可能会写脚本来主动执行工作。有些网站因为须要用户登陆,所以脚本的主动登陆性能必不可少。

不过咱们在登陆网站的时候常常会呈现验证码,验证码的目标就是为了避免机器登陆、自动化脚本操作,那么有没有方法让脚本能自动识别验证码实现登陆呢?

接下来我以 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 和 challenge
    const 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/openHackin…

原文:https://lwebapp.com/zh/post/b…

可能还有很多待优化的中央,欢送大家指出。

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

参考

  • Nodejs Playwright 2Captcha 验证码辨认实现主动登陆
  • Playwright
  • 2Captcha
  • Python 主动登录哔哩哔哩(2captcha 打码平台)

正文完
 0