Nodejs Playwright 2Captcha 驗(yàn)證碼識(shí)別實(shí)現(xiàn)自動(dòng)登陸功能
Nodejs Playwright 2Captcha 驗(yàn)證碼識(shí)別實(shí)現(xiàn)自動(dòng)登陸
需求
日常工作當(dāng)中,為了提高工作效率,我們可能會(huì)寫(xiě)腳本來(lái)自動(dòng)執(zhí)行任務(wù)。有些網(wǎng)站因?yàn)樾枰脩?hù)登陸,所以腳本的自動(dòng)登陸功能必不可少。
不過(guò)我們?cè)诘顷懢W(wǎng)站的時(shí)候經(jīng)常會(huì)出現(xiàn)驗(yàn)證碼,驗(yàn)證碼的目的就是為了防止機(jī)器登陸、自動(dòng)化腳本操作,那么有沒(méi)有辦法讓腳本能自動(dòng)識(shí)別驗(yàn)證碼實(shí)現(xiàn)登陸呢?
接下來(lái)我以 B 站為例給大家講解下,如何解決自動(dòng)登陸腳本中最關(guān)鍵的驗(yàn)證碼問(wèn)題。
探索
首先需要體驗(yàn)下這個(gè)網(wǎng)站的登陸方式,了解下它的驗(yàn)證碼類(lèi)型。
打開(kāi) B 站 https://www.bilibili.com/ ,打開(kāi)控制臺(tái),點(diǎn)擊登陸,這時(shí)候會(huì)彈出中間小的登陸框,通常輸入完賬號(hào)和密碼,就會(huì)彈出驗(yàn)證碼框了,我們猜測(cè)驗(yàn)證碼的接口此時(shí)已經(jīng)請(qǐng)求了。
由于驗(yàn)證碼的英文是 captcha
,我們?cè)?network
面板里搜 captcha
發(fā)現(xiàn)了一個(gè)和驗(yàn)證碼相關(guān)的接口
https://passport.bilibili.com/x/passport-login/captcha
點(diǎn)開(kāi)看接口返回結(jié)果,果然有一些有用的信息,我們發(fā)現(xiàn)驗(yàn)證碼類(lèi)型是 geetest
。
{ "code": 0, "message": "0", "ttl": 1, "data": { "type": "geetest", "token": "b416c387953540608bb5da384b4e372b", "geetest": { "challenge": "aeb4653fb336f5dcd63baecb0d51a1f3", "gt": "ac597a4506fee079629df5d8b66dd4fe" }, "tencent": { "appid": "" } } }
通過(guò)搜索發(fā)現(xiàn)了 B 站使用的驗(yàn)證碼服務(wù)是 geetest
提供的,國(guó)內(nèi)有很多網(wǎng)站都用的這個(gè)服務(wù), geetest
驗(yàn)證碼的特點(diǎn)是移動(dòng)拼圖、按順序選擇文字或者數(shù)字。
那么接下來(lái),就來(lái)尋找可以識(shí)別 geetest
驗(yàn)證碼的辦法。
小編了解了市面上提供的驗(yàn)證碼解決方案,效果比較好的基本都是 OCR 服務(wù)商,對(duì)比之后發(fā)現(xiàn) 2Captcha 的服務(wù)非常好,解碼速度快、服務(wù)器連接穩(wěn)定、支持多種語(yǔ)言 API、價(jià)格公道,小編決定試用下 2Captcha
。
接下來(lái)就展示使用 Nodejs
+ Playwright
+ 2Captcha
來(lái)解決 B 站的登陸驗(yàn)證碼問(wèn)題。
如果想用其他語(yǔ)言和框架,比如
Python
+Selenium
,也可以參照這個(gè)教程,解決問(wèn)題的思路都是一樣的。
解決
1.如何識(shí)別驗(yàn)證碼
先仔細(xì)閱讀官方文檔 2Captcha API Geetest,解決方案寫(xiě)的很詳細(xì),簡(jiǎn)單來(lái)說(shuō)
- 通過(guò)攔截網(wǎng)站接口,獲取
gt
、challenge
這兩個(gè)校驗(yàn)碼參數(shù),請(qǐng)求http://2captcha.com/in.php
,得到驗(yàn)證碼ID
- 隔一段時(shí)間再請(qǐng)求
http://2captcha.com/res.php
,得到校驗(yàn)成功的標(biāo)識(shí)challenge
、validate
、seccode
1.如何應(yīng)用驗(yàn)證結(jié)果
拿到最關(guān)鍵的 validate
之后,模擬用戶(hù)填寫(xiě)賬號(hào)密碼登陸,攔截驗(yàn)證碼請(qǐng)求接口的返回參數(shù),替換為校驗(yàn)成功的參數(shù),隨即觸發(fā)登陸接口。
接下來(lái),我們分析下詳細(xì)步驟
環(huán)境準(zhǔn)備
我們先搭建一下腳本執(zhí)行環(huán)境。
我們使用 Node.js
+ Playwright
來(lái)寫(xiě)腳本。
- 先確保你的電腦本地已經(jīng)安裝了 Nodejs
- 再新建空項(xiàng)目,安裝
Playwright
mkdir bypass-captcha cd bypass-captcha npm init npm i -D playwright
我們采用
Playwright
的庫(kù)模式,詳細(xì)文檔:Playwright
1.在項(xiàng)目根目錄新建一個(gè)腳本文件 captcha.js
,填入以下內(nèi)容,命令行運(yùn)行node captcha.js
來(lái)簡(jiǎn)單測(cè)試下是否能正常啟動(dòng)項(xiàng)目
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(); })();
正常情況下會(huì)彈出一個(gè)谷歌瀏覽器界面,顯示 B 站首頁(yè),隨后瀏覽器自動(dòng)關(guān)閉。
請(qǐng)求 in.php 接口
首先整理下請(qǐng)求http://2captcha.com/in.php
接口所需要的參數(shù)有哪些,可以看下參數(shù)列表,我們關(guān)注下必傳參數(shù)
參數(shù) | 類(lèi)型 | 必選 | 描述 |
---|---|---|---|
key | String | 是 | 您的 API key |
method | String | 是 | geetest - 定義您正在發(fā)送的是 Geetest 的驗(yàn)證碼 |
gt | String | 是 | 在目標(biāo)網(wǎng)站上找到的 gt 參數(shù) |
challenge | String | 是 | 在目標(biāo)網(wǎng)站上找到的 challenge 參數(shù) |
api_server | String | 否 | 在目標(biāo)網(wǎng)站上找到的 api_server 參數(shù) |
pageurl | String | 是 | 您看到 Geetest 驗(yàn)證碼時(shí)所在網(wǎng)頁(yè)的完整 URL |
header_acao | Integer 默認(rèn): 0 | 否 | 0 - 禁用 1 - 啟用。 如果啟用 in.php 將在響應(yīng)中包含 Access-Control-Allow-Origin:* 標(biāo)頭。 用于 Web 應(yīng)用程序中的跨域 AJAX 請(qǐng)求。 res.php 也支持 |
pingback | String | 否 | 解決驗(yàn)證碼時(shí)將發(fā)送的 pingback(回調(diào))響應(yīng)的 URL。 URL 應(yīng)該在服務(wù)器上注冊(cè)。 更多信息在這里 |
json | Integer 默認(rèn): 0 | 否 | 0 - 服務(wù)器將以純文本形式發(fā)送響應(yīng) 1 - 告訴服務(wù)器以 JSON 格式發(fā)送響應(yīng) |
soft_id | Integer | 否 | 軟件開(kāi)發(fā)人員的 ID。 將他們的軟件與 2Captcha 集成的開(kāi)發(fā)人員將獲得獎(jiǎng)勵(lì):軟件用戶(hù)支出的 10%。 |
proxy | String | 否 | 格式: login:password@123.123.123.123:3128 您可以找到更多關(guān)于代理的信息這里。 |
proxytype | String | 否 | 代理類(lèi)型: HTTP, HTTPS, SOCKS4, SOCKS5. |
userAgent | String | 否 | 您的 userAgent 將傳遞給我們的工作人員并用于解決驗(yàn)證碼。 |
key
是需要在 2Captcha 官網(wǎng)注冊(cè)賬戶(hù)后,后臺(tái)面板的賬戶(hù)設(shè)置中就有一個(gè)API key
,當(dāng)然要起讓 key 生效的話(huà)還需要充值一定金額method
是一個(gè)固定值geetest
gt
和challenge
之前已經(jīng)在網(wǎng)站登錄頁(yè)面的接口中看到了。不過(guò)這里有個(gè)注意點(diǎn),gt
是每個(gè)網(wǎng)站只有一個(gè)值,B 站這里是固定的ac597a4506fee079629df5d8b66dd4fe
,但是challenge
是一個(gè)動(dòng)態(tài)值,每次 API 請(qǐng)求都會(huì)獲得一個(gè)新的challenge
值。在頁(yè)面上加載驗(yàn)證碼后,challenge
值就會(huì)失效。 所以要在網(wǎng)站登錄頁(yè)加載的時(shí)候監(jiān)聽(tīng)https://passport.bilibili.com/x/passport-login/captcha
這個(gè)請(qǐng)求,每次重新識(shí)別出新的challenge
值。下面會(huì)講解如何監(jiān)聽(tīng)到。pageurl
就是登錄頁(yè)的地址https://www.bilibili.com/
于是我們可以得到類(lèi)似這樣一個(gè)請(qǐng)求接口
http://2captcha.com/in.php?key=1abc234de56fab7c89012d34e56fa7b8&method=geetest>=ac597a4506fee079629df5d8b66dd4fe&challenge=12345678abc90123d45678ef90123a456b&pageurl=https://www.bilibili.com/
1.接下來(lái)就解決每次進(jìn)首頁(yè)獲取新的 challenge
值
模擬用戶(hù)點(diǎn)擊登錄的流程
- 先啟動(dòng)谷歌瀏覽器,打開(kāi) B 站首頁(yè)
- 點(diǎn)擊頂部的登錄按鈕,會(huì)彈出登錄框
- 這時(shí)候驗(yàn)證碼接口已經(jīng)發(fā)出,在這里監(jiān)聽(tīng)驗(yàn)證碼接口返回的參數(shù),就能截獲到
gt
和challenge
的值
const { chromium } = require("playwright"); (async () => { // 選擇Chrome瀏覽器,設(shè)置headless: false 能看到瀏覽器界面 const browser = await chromium.launch({ headless: false, }); const page = await browser.newPage(); // 打開(kāi)B站 await page.goto("https://www.bilibili.com/"); const [response] = await Promise.all([ // 請(qǐng)求驗(yàn)證碼接口 page.waitForResponse( (response) => response.url().includes("/x/passport-login/captcha") && response.status() === 200 ), // 點(diǎn)擊頂部的登錄按鈕 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秒,防止瀏覽器關(guān)閉太快,來(lái)不及看到效果 sleep(5000); // 關(guān)閉瀏覽器 await browser.close(); })(); /** * 模擬sleep功能,延遲一定時(shí)間,單位毫秒 * Delay for a number of milliseconds */ function sleep(delay) { var start = new Date().getTime(); while (new Date().getTime() < start + delay); }
1.使用request
庫(kù)來(lái)單獨(dú)請(qǐng)求in.php
接口
先安裝 request
npm i request
現(xiàn)在可以開(kāi)始正式的請(qǐng)求 http://2captcha.com/in.php
接口了
// 請(qǐng)求 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); } } );
正常情況下,這時(shí)候會(huì)返回驗(yàn)證碼 ID
,比如 {"status":1,"request":"2122988149"}
,取 request
字段即可。
如果接口返回代碼
ERROR_ZERO_BALANCE
,表明您的賬戶(hù)余額不足,需要充值,我這里充值了最低額度用于演示,大家根據(jù)自己需要適當(dāng)體驗(yàn)下。
擴(kuò)展學(xué)習(xí)
1.為了提升安全性,我們將 API Key
寫(xiě)在環(huán)境變量文件中來(lái)引用。
在根目錄新建一個(gè)環(huán)境變量文件.env
,寫(xiě)入API Key
的值
# .env文件 API_KEY="d34y92u74en96yu6530t5p2i2oe3oqy9"
1.然后安裝dotenv
庫(kù),用來(lái)獲取到環(huán)境變量
npm i dotenv
1.在 js 腳本中使用
require("dotenv").config();
這樣通過(guò) process.env.API_KEY
就能取到 .env
中的變量了,通常 .env
文件不上傳到代碼倉(cāng)庫(kù),保證個(gè)人信息的安全性。
如果不想把信息寫(xiě)到文件中的同時(shí)確保安全性,也可以直接在控制臺(tái)輸入傳入 Node.js 環(huán)境變量,比如
API_KEY=d34y92u74en96yu6530t5p2i2oe3oqy9 node captcha.js
請(qǐng)求 res.php
接口請(qǐng)求接口之前,我們也整理下所需參數(shù)
GET 參數(shù) | 類(lèi)型 | 必選 | 描述 |
---|---|---|---|
key | String | 是 | 您的 API key |
action | String | 是 | get - 得到您的驗(yàn)證碼的答案 |
id | Integer | 是 | in.php 返回的驗(yàn)證碼 ID |
json | Integer 默認(rèn): 1 | 否 | 服務(wù)器將始終以 JSON 格式返回 Geetest 驗(yàn)證碼的響應(yīng)。 |
key
就是API_KEY
,上一個(gè)接口也用到了action
就是固定值get
id
是剛剛in.php
返回的驗(yàn)證碼ID
???????上一個(gè)請(qǐng)求 20 秒之后,再請(qǐng)求 http://2captcha.com/res.php
接口獲取驗(yàn)證結(jié)果
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); } } } );
接口會(huì)返回三個(gè)值 challenge
、 validate
和 seccode
,每一個(gè)參數(shù)都是一個(gè)字符串
{ "geetest_challenge": "aeb4653fb336f5dcd63baecb0d51a1f3", "geetest_validate": "9f36e8f3a928a7d382dad8f6c1b10429", "geetest_seccode": "9f36e8f3a928a7d382dad8f6c1b10429|jordan" }
其中 challenge
就是前面我們攔截到的參數(shù), validate
是校驗(yàn)結(jié)果標(biāo)識(shí), seccode
內(nèi)容和 validate
基本一致,只多了一個(gè)單詞。我們需要將 validate
存儲(chǔ)下來(lái)備用。
這里有時(shí)候會(huì)碰到驗(yàn)證碼無(wú)法校驗(yàn)通過(guò)的情況,可以多嘗試幾次,或者聯(lián)系 2Captcha 官網(wǎng)排查問(wèn)題
到這里,驗(yàn)證碼校驗(yàn)結(jié)果的信息已經(jīng)獲取完整,接下來(lái)就是拿校驗(yàn)結(jié)果去登陸了。
登陸
1.先來(lái)研究下正常用戶(hù)點(diǎn)擊驗(yàn)證碼校驗(yàn)成功后的登陸流程
我們發(fā)現(xiàn)了三個(gè)接口
https://api.geetest.com/ajax.php
:驗(yàn)證碼接口,用于生成驗(yàn)證碼和校驗(yàn)驗(yàn)證碼是否通過(guò)。校驗(yàn)接口返回?cái)?shù)據(jù)中的 validate
字段就是 2Captcha 服務(wù)獲取到的 geetest_validate
。
https://passport.bilibili.com/x/passport-login/web/key?_=1649087831803
:密碼加密接口,用于獲取 hash 和公鑰
https://passport.bilibili.com/x/passport-login/web/login
:登陸接口,入?yún)ㄙ~號(hào)、密碼、token
、challenge
、validate
和 seccode
等
我們分析這幾個(gè)接口,可以得出兩個(gè)登陸方案。
- 第一個(gè)方案,在
Node.js
環(huán)境下請(qǐng)求加密接口和登陸接口,獲取用戶(hù) Cookie 信息,后續(xù)用戶(hù)登陸直接攜帶 Cookie 信息即可。這個(gè)方案的難點(diǎn)是需要單獨(dú)處理密碼加密,對(duì)新手不太友好。 - 第二個(gè)方案,用
Playwright
模擬用戶(hù)填寫(xiě)賬號(hào)密碼登陸,隨機(jī)點(diǎn)擊驗(yàn)證碼觸發(fā)登陸,攔截驗(yàn)證碼接口的返回參數(shù),替換為校驗(yàn)成功的標(biāo)識(shí),隨即觸發(fā)登陸接口。
我們采用第二種方案。
不過(guò)也遇到一個(gè)坑,就是 Node.js
環(huán)境下,驗(yàn)證碼圖片加載不出來(lái),后面發(fā)現(xiàn)驗(yàn)證碼接口 https://api.geetest.com/ajax.php
同時(shí)負(fù)責(zé)拉取驗(yàn)證碼圖片和校驗(yàn)驗(yàn)證碼,我們直接攔截拉取驗(yàn)證碼圖片時(shí)的請(qǐng)求,替換校驗(yàn)結(jié)果就能觸發(fā)登陸了,不需要等圖片驗(yàn)證碼出來(lái)。這個(gè)細(xì)節(jié)很關(guān)鍵。
到此這篇關(guān)于Nodejs Playwright 2Captcha 驗(yàn)證碼識(shí)別實(shí)現(xiàn)自動(dòng)登陸功能的文章就介紹到這了,更多相關(guān)Nodejs Playwright 2Captcha 驗(yàn)證碼識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用vs code開(kāi)發(fā)Nodejs程序的使用方法
本篇文章主要介紹了使用vs code開(kāi)發(fā)Nodejs程序的使用方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09node.js中stream流中可讀流和可寫(xiě)流的實(shí)現(xiàn)與使用方法實(shí)例分析
這篇文章主要介紹了node.js中stream流中可讀流和可寫(xiě)流的實(shí)現(xiàn)與使用方法,結(jié)合實(shí)例形式分析了node.js stream流可讀流和可寫(xiě)流基本分類(lèi)、原理、定義、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2020-02-02Nodejs監(jiān)聽(tīng)日志文件的變化的過(guò)程解析
最近有在做日志文件的分析,其中有一個(gè)需求:A服務(wù)器項(xiàng)目需要用Nodejs監(jiān)聽(tīng)日志文件的變化,當(dāng)項(xiàng)目產(chǎn)生了新的日志信息,將新的部分通過(guò)socket傳輸?shù)紹服務(wù)器項(xiàng)目,本文重點(diǎn)給大家介紹Nodejs監(jiān)聽(tīng)日志文件的變化的相關(guān)知識(shí),一起看看吧2019-08-08nodejs進(jìn)階(6)—連接MySQL數(shù)據(jù)庫(kù)示例
本篇文章主要介紹了nodejs進(jìn)階(6)—連接MySQL數(shù)據(jù)庫(kù)示例,詳細(xì)的介紹了NodeJS操作MySQL數(shù)據(jù)庫(kù),作為應(yīng)用最為廣泛的開(kāi)源數(shù)據(jù)庫(kù)則成為我們的首選,有興趣的可以了解一下。2017-01-01node.js中的fs.readdirSync方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.readdirSync方法使用說(shuō)明,本文介紹了fs.readdirSync方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12