使用puppeteer破解極驗(yàn)的滑動(dòng)驗(yàn)證碼
基本的流程:
1. 打開(kāi)前端網(wǎng),點(diǎn)擊登錄。
2. 填寫(xiě)賬號(hào),密碼。
3. 點(diǎn)解驗(yàn)證按鈕,通過(guò)滑動(dòng)驗(yàn)證,最后成功登陸。
代碼實(shí)現(xiàn):
github上可以checkout。
具體代碼如下所示:
run.js
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6 Plus'];
let timeout = function (delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(1)
} catch (e) {
reject(0)
}
}, delay);
})
}
let page = null
let btn_position = null
let times = 0 // 執(zhí)行重新滑動(dòng)的次數(shù)
const distanceError = [-10,2,3,5] // 距離誤差
async function run() {
const browser = await puppeteer.launch({
headless:false //這里我設(shè)置成false主要是為了讓大家看到效果,設(shè)置為true就不會(huì)打開(kāi)瀏覽器
});
page = await browser.newPage();
// 1.打開(kāi)前端網(wǎng)
await page.emulate(iPhone);
await page.goto('https://www.qdfuns.com/');
await timeout(1000);
// 2.打開(kāi)登錄頁(yè)面
page.click('a[data-type=login]')
await timeout(1000);
// 3.輸入賬號(hào)密碼
page.type('input[data-type=email]','你的賬號(hào)')
await timeout(500);
page.type('input[placeholder=密碼]','你的密碼')
await timeout(1000);
// 4.點(diǎn)擊驗(yàn)證
page.click('.geetest_radar_tip')
await timeout(1000);
btn_position = await getBtnPosition();
// 5.滑動(dòng)
drag(null)
}
/**
* 計(jì)算按鈕需要滑動(dòng)的距離
* */
async function calculateDistance() {
const distance = await page.evaluate(() => {
// 比較像素,找到缺口的大概位置
function compare(document) {
const ctx1 = document.querySelector('.geetest_canvas_fullbg'); // 完成圖片
const ctx2 = document.querySelector('.geetest_canvas_bg'); // 帶缺口圖片
const pixelDifference = 30; // 像素差
let res = []; // 保存像素差較大的x坐標(biāo)
// 對(duì)比像素
for(let i=57;i<260;i++){
for(let j=1;j<160;j++) {
const imgData1 = ctx1.getContext("2d").getImageData(1*i,1*j,1,1)
const imgData2 = ctx2.getContext("2d").getImageData(1*i,1*j,1,1)
const data1 = imgData1.data;
const data2 = imgData2.data;
const res1=Math.abs(data1[0]-data2[0]);
const res2=Math.abs(data1[1]-data2[1]);
const res3=Math.abs(data1[2]-data2[2]);
if(!(res1 < pixelDifference && res2 < pixelDifference && res3 < pixelDifference)) {
if(!res.includes(i)) {
res.push(i);
}
}
}
}
// 返回像素差最大值跟最小值,經(jīng)過(guò)調(diào)試最小值往左小7像素,最大值往左54像素
return {min:res[0]-7,max:res[res.length-1]-54}
}
return compare(document)
})
return distance;
}
/**
* 計(jì)算滑塊位置
*/
async function getBtnPosition() {
const btn_position = await page.evaluate(() => {
const {clientWidth,clientHeight} = document.querySelector('.geetest_popup_ghost')
return {btn_left:clientWidth/2-104,btn_top:clientHeight/2+59}
})
return btn_position;
}
/**
* 嘗試滑動(dòng)按鈕
* @param distance 滑動(dòng)距離
* */
async function tryValidation(distance) {
//將距離拆分成兩段,模擬正常人的行為
const distance1 = distance - 10
const distance2 = 10
page.mouse.click(btn_position.btn_left,btn_position.btn_top,{delay:2000})
page.mouse.down(btn_position.btn_left,btn_position.btn_top)
page.mouse.move(btn_position.btn_left+distance1,btn_position.btn_top,{steps:30})
await timeout(800);
page.mouse.move(btn_position.btn_left+distance1+distance2,btn_position.btn_top,{steps:20})
await timeout(800);
page.mouse.up()
await timeout(4000);
// 判斷是否驗(yàn)證成功
const isSuccess = await page.evaluate(() => {
return document.querySelector('.geetest_success_radar_tip_content') && document.querySelector('.geetest_success_radar_tip_content').innerHTML
})
await timeout(1000);
// 判斷是否需要重新計(jì)算距離
const reDistance = await page.evaluate(() => {
return document.querySelector('.geetest_result_content') && document.querySelector('.geetest_result_content').innerHTML
})
await timeout(1000);
return {isSuccess:isSuccess==='驗(yàn)證成功',reDistance:reDistance.includes('怪物吃了拼圖')}
}
/**
* 拖動(dòng)滑塊
* @param distance 滑動(dòng)距離
* */
async function drag(distance) {
distance = distance || await calculateDistance();
const result = await tryValidation(distance.min)
if(result.isSuccess) {
await timeout(1000);
//登錄
console.log('驗(yàn)證成功')
page.click('#modal-member-login button')
}else if(result.reDistance) {
console.log('重新計(jì)算滑距離錄,重新滑動(dòng)')
times = 0
await drag(null)
} else {
if(distanceError[times]){
times ++
console.log('重新滑動(dòng)')
await drag({min:distance.max,max:distance.max+distanceError[times]})
} else {
console.log('滑動(dòng)失敗')
times = 0
run()
}
}
}
run()
package.json
{
"name": "demo",
"version": "1.0.0",
"dependencies": {
"puppeteer": "^1.0.0"
}
}
運(yùn)行
1. 將這個(gè)兩個(gè)文件保存到文件夾下面,終端切換到當(dāng)前路徑下
2. npm i
3. 補(bǔ)上前端網(wǎng)的賬號(hào),密碼
4. node run
演示
下圖演示可以分為四步:
1. 打開(kāi)登陸頁(yè)面,輸入事先寫(xiě)好的賬號(hào)密碼。
2. 第一次拖動(dòng)滑塊提示“被怪獸吃了”,所以重新計(jì)算了新的圖片的缺口距離。
3. 第二,三次拖動(dòng)提示“沒(méi)正確合拼”,所以重新拖動(dòng)。
4. 驗(yàn)證成功,登錄。
(請(qǐng)將鼠標(biāo)放到gif上查看演示效果,或者請(qǐng)拖到新窗口打開(kāi)gif)
說(shuō)明
1. 滑動(dòng)驗(yàn)證有三個(gè)canvas,其中只需要 classname為‘geetest_canvas_fullbg'以及‘geetest_canvas_bg'的進(jìn)行像素差對(duì)比。ps:前者是完整圖片,后者是帶缺口的圖片。
2. 每個(gè)帶缺口的圖片都有一塊誤導(dǎo)的陰影,所以對(duì)比像素差的時(shí)候,計(jì)算出的距離分別是誤導(dǎo)陰影以及缺口的。因此,滑動(dòng)距離的取值,我取‘{min:res[0]-7,max:res[res.length-1]-54}'。當(dāng)缺口比誤導(dǎo)陰影靠左時(shí), min(距離最小值) 值就是滑動(dòng)距離,否則就是 max(距離最大值)減去滑塊寬度 。
3. 滑動(dòng)結(jié)果分三種情況:驗(yàn)證成功,被吃了,失敗。 “被吃了” 會(huì)重新請(qǐng)求圖片,所以重新計(jì)算了距離再滑動(dòng); “失敗” 則重新滑動(dòng),如果執(zhí)行 4 次依然失敗,則重新run整個(gè)流程。
總結(jié)
以上所述是小編給大家介紹的使用puppeteer破解極驗(yàn)的滑動(dòng)驗(yàn)證碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Node.js中文件操作模塊File System的詳細(xì)介紹
FileSystem模塊是類(lèi)似UNIX(POSIX)標(biāo)準(zhǔn)的文件操作API,用于操作文件系統(tǒng)——讀寫(xiě)目錄、讀寫(xiě)文件——Node.js底層使用C程序來(lái)實(shí)現(xiàn),這些功能是客戶(hù)端JS所不具備的。下面這篇文章就給大家詳細(xì)介紹了Node.js中的文件操作模塊File System,有需要的朋友們可以參考借鑒。2017-01-01
NodeJS后端開(kāi)發(fā)操作文件之讀寫(xiě)文件
這篇文章主要介紹了NodeJS后端開(kāi)發(fā)操作文件之讀寫(xiě)文件,操作文件是服務(wù)端一個(gè)基礎(chǔ)的功能,也是做后端開(kāi)發(fā)的必備能力之一,操作文件主要包括讀和寫(xiě)。而這些功能NodeJS都已經(jīng)提供了對(duì)應(yīng)的方法2022-06-06
nodejs express搭建服務(wù)并熱更新文件過(guò)程詳解
這篇文章主要為大家介紹了nodejs express搭建服務(wù)并熱更新文件過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
node微信小程序登錄實(shí)現(xiàn)登錄的項(xiàng)目實(shí)踐
登陸流程是指小程序用戶(hù)進(jìn)行授權(quán)登陸,即獲取用戶(hù)的微信賬號(hào)等信息本文就來(lái)介紹一下node微信小程序登錄實(shí)現(xiàn)登錄,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
Node實(shí)現(xiàn)前端本地開(kāi)發(fā)接口代理服務(wù)
本文主要介紹了Node實(shí)現(xiàn)前端本地開(kāi)發(fā)接口代理服務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05

