上一篇文章寫了一個puppeteer的簡單入門,出於好奇想了一個問題,puppeteer能破解驗證碼嗎???於是,正好就拿前端網來試試 (純粹出於學習)
基本的流程:
1. 開啟前端網,點選登入。
2. 填寫賬號,密碼。
3. 點解驗證按鈕,通過滑動驗證,最後成功登陸。
程式碼實現:
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 // 執行重新滑動的次數
const distanceError = [-10,2,3,5] // 距離誤差
async function run() {
const browser = await puppeteer.launch({
headless:false //這裡我設定成false主要是為了讓大家看到效果,設定為true就不會開啟瀏覽器
});
page = await browser.newPage();
// 1.開啟前端網
await page.emulate(iPhone);
await page.goto('https://www.qdfuns.com/');
await timeout(1000);
// 2.開啟登入頁面
page.click('a[data-type=login]')
await timeout(1000);
// 3.輸入賬號密碼
page.type('input[data-type=email]','你的賬號')
await timeout(500);
page.type('input[placeholder=密碼]','你的密碼')
await timeout(1000);
// 4.點選驗證
page.click('.geetest_radar_tip')
await timeout(1000);
btn_position = await getBtnPosition();
// 5.滑動
drag(null)
}
/**
* 計算按鈕需要滑動的距離
* */
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座標
// 對比畫素
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);
}
}
}
}
// 返回畫素差最大值跟最小值,經過除錯最小值往左小7畫素,最大值往左54畫素
return {min:res[0]-7,max:res[res.length-1]-54}
}
return compare(document)
})
return distance;
}
/**
* 計算滑塊位置
*/
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;
}
/**
* 嘗試滑動按鈕
* @param distance 滑動距離
* */
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);
// 判斷是否驗證成功
const isSuccess = await page.evaluate(() => {
return document.querySelector('.geetest_success_radar_tip_content') && document.querySelector('.geetest_success_radar_tip_content').innerHTML
})
await timeout(1000);
// 判斷是否需要重新計算距離
const reDistance = await page.evaluate(() => {
return document.querySelector('.geetest_result_content') && document.querySelector('.geetest_result_content').innerHTML
})
await timeout(1000);
return {isSuccess:isSuccess==='驗證成功',reDistance:reDistance.includes('怪物吃了拼圖')}
}
/**
* 拖動滑塊
* @param distance 滑動距離
* */
async function drag(distance) {
distance = distance || await calculateDistance();
const result = await tryValidation(distance.min)
if(result.isSuccess) {
await timeout(1000);
//登入
console.log('驗證成功')
page.click('#modal-member-login button')
}else if(result.reDistance) {
console.log('重新計算滑距離錄,重新滑動')
times = 0
await drag(null)
} else {
if(distanceError[times]){
times ++
console.log('重新滑動')
await drag({min:distance.max,max:distance.max+distanceError[times]})
} else {
console.log('滑動失敗')
times = 0
run()
}
}
}
run()
複製程式碼
package.json
{
"name": "demo",
"version": "1.0.0",
"dependencies": {
"puppeteer": "^1.0.0"
}
}
複製程式碼
執行
1. 將這個兩個檔案儲存到資料夾下面,終端切換到當前路徑下
2. npm i
3. 補上前端網的賬號,密碼
4. node run
演示
下圖演示可以分為四步:
1. 開啟登陸頁面,輸入事先寫好的 賬號密碼 。
2. 第一次拖動滑塊提示“ 被怪獸吃了”,所以重新計算了新的圖片的缺口距離。
3. 第二,三次拖動提示 “沒正確合拼”,所以重新拖動。
4. 驗證成功,登入。
(請將滑鼠放到gif上檢視演示效果,或者請拖到新視窗開啟gif)