引子
不知道大家是否還記得之前讓我刷讚的小朋友,自從他回到學校後,可能是由於學業太忙,把我們組成刷贊團隊的事情忘記了,只是每天早上給我發信息幫他刷贊,遲遲沒有新的用戶,刷贊就沒有量產的動力,我怠慢了幾天沒給他刷,他就不再理我了,我覺得是中了他的圈套,他只是嘴上說說要發展團隊,其實只是貪戀我每天給他刷幾萬個贊。
效果
selnium過滑動驗證碼
代碼
const webdriver = require('selenium-webdriver')
const firefox = require('selenium-webdriver/firefox');
const fs = require('fs')
const getPixels = require("get-pixels")
const ndarray = require("ndarray")
!async function(){
// 新建一個 firefox 的 driver 實例
var options = new firefox.Options()
.setProfile('C:/Users/2bt/AppData/Roaming/Mozilla/Firefox/Profiles/m1mwcwm8.default')
var driver = await new webdriver.Builder().forBrowser('firefox').setFirefoxOptions(options).build()
console.log('開始模擬操作,命令提示符跳動代表着等待網頁過程,默認都是10秒。如果網絡較差,可能導致錯誤。')
console.log('———————————————————————————————————————————————————————————————————————————————')
main();
async function main(){
var distance = 0, dragX = 0 ,bgX =0 ,errX=0;
// 訪問極驗demo頁
await driver.get('http://www.jg520.com/?fid=1&tid=2095')
var searchBox = driver.wait(webdriver.until.elementLocated(webdriver.By.id('inputvalue')), 10000)
searchBox.sendKeys('809101180');
await driver.sleep(500)
console.log('填寫QQ號')
var btn = await driver.wait(webdriver.until.elementLocated(webdriver.By.id('submit_buy')), 10000)
await driver.sleep(5000)
await btn.click();
console.log('點擊免費領取,等待驗證碼')
try{
await driver.wait(webdriver.until.alertIsPresent(),5000);
let alert = await driver.switchTo().alert();
let alertText = await alert.getText();
if(alertText) console.log('每天限領一次,已領取,結束進程。')
await alert.accept();
return 0;
}catch(err){}
btn = await driver.wait(webdriver.until.elementLocated(webdriver.By.css('.geetest_radar_tip')), 10000)
await btn.click();
await driver.sleep(3000)
//找到驗證碼背景圖元素, 是一個 canvas
const bgCanvas = await driver.wait(webdriver.until.elementLocated(webdriver.By.css('.geetest_window')), 30000)
//1 截取bg圖片
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 1;document.querySelector(".geetest_canvas_slice").style.opacity = 0;')
const bgPng = await bgCanvas.takeScreenshot()
//取消隱藏
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 1;document.querySelector(".geetest_canvas_slice").style.opacity = 1')
getPixels('data:image/png;base64,'+bgPng,function(err, pixels) {
if(err) {
console.log("讀取背景缺口位置錯誤")
return
}
bgX = getBoundary(pixels,getGrey(pixels)/10*3)
//console.log('讀取背景缺口邊界X座標: ',bgX);
})
// 獲取拼圖滑塊按鈕
const button = await driver.wait(webdriver.until.elementLocated(webdriver.By.css('.geetest_slider_button')), 30000)
// 初始化 action
let actions = driver.actions({async: true})
console.log('開始計算拖動位置')
await driver.sleep(3000)
// 把鼠標移動到滑塊上, 然後點擊
await actions.move({
origin: button,
duration: 1000
}).pause(100).press().move({
origin: button,
x: 1,
duration: 10
}).pause(300).perform()
//截取slice圖片,用於計算拖動後產生的隨機位移
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 0;document.querySelector(".geetest_canvas_slice").style.opacity = 1;')
const slice = await bgCanvas.takeScreenshot()
//取消隱藏
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 1;document.querySelector(".geetest_canvas_slice").style.opacity = 1')
getPixels('data:image/png;base64,'+slice.toString(),function(err, pixels) {
if(err) {
console.log("讀取初始位置錯誤")
return
}
dragX = getBoundary(pixels,360)
})
await driver.sleep(500)
distance = bgX - dragX;
var arr = getTrack(distance*0.8+20)
//乘以0.8是因爲計算出的值是以截取的canvas爲基礎的,真實拖動的距離需要乘以5:4這個比值
console.log('開始拖動滑塊,需要滑動的距離: ',distance*0.8+20)
var backArr = [-4,-5,-6,-4,-2,-1]
await moveTrack(arr,backArr)
//4 再次截取slice圖片,看看那裏出錯了
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 0;document.querySelector(".geetest_canvas_slice").style.opacity = 1;')
const err = await bgCanvas.takeScreenshot()
//取消隱藏
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 1;document.querySelector(".geetest_canvas_slice").style.opacity = 1')
getPixels('data:image/png;base64,'+err,function(err, pixels) {
if(err){
console.log('獲取最終位置錯誤')
return
}
errX = getBoundary(pixels,360)
console.log('完成移動到: ',errX,',錯位: ',(errX - bgX)*0.8);
})
await driver.sleep(3000)
const successTip = driver.wait(webdriver.until.elementLocated(webdriver.By.id('layui-layer10'),10000)).getText()
if(successTip === '領取成功,等待到賬,請收藏本站網址每天領福利,好東西一定要分享給好友哦!'){
console.log('成功~')
}else{
console.log('失敗~重新操作')
await driver.sleep(3000)
main()
}
}
async function moveTrack(arr,backArr){
let actions = driver.actions({bridge: true})
for(i=0;i<arr.length;i++){
var random = Math.floor(Math.random()*200+30)
await actions.move({
origin: webdriver.Origin.POINTER,
x:arr[i],
duration:random
})
}
await actions.pause(100)
for(i=0;i<backArr.length;i++){
var random = Math.floor(Math.random()*200+30)
await actions.move({
origin: webdriver.Origin.POINTER,
x:backArr[i],
duration:random
})
}
await actions.release().perform()
}
async function move(distance){
await driver.sleep(1000)
let actions = driver.actions({bridge: true})
await actions.move({
origin: webdriver.Origin.POINTER,
x:distance,
duration:1000
}).release().perform()
console.log("滑動距離: ",distance)
}
async function getStep(){
//截取slice圖片,用於計算拖動後產生的隨機位移
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 0;document.querySelector(".geetest_canvas_slice").style.opacity = 1;')
const slice = await bgCanvas.takeScreenshot()
//取消隱藏
await driver.executeScript('document.querySelector(".geetest_canvas_bg").style.opacity = 1;document.querySelector(".geetest_canvas_slice").style.opacity = 1')
getPixels('data:image/png;base64,'+slice.toString(),function(err, pixels) {
if(err) {
console.log("讀取初始位置錯誤")
return
}
dragX = getBoundary(pixels,360)
console.log('getStep函數返回:' ,dragX)
})
}
//取模擬拖動滑塊的數據
//用等差數列代替了勻加速運動
//因爲勻加速運動取得的小數經過四捨五入會出現不精確,導致拖動的距離出差錯
function getTrack(distance){
var track = [],d=2,n=0,s=0,S=0
while(S<distance){
s=1+n;
n=n+d;
S+=s;
track.push(s)
}
var diff = (S - distance);
track.splice(-1,1,Math.round(Number(track.slice(-1))-diff))
return track;
}
//getBoundary查找邊界函數。
//參數
//pixels是get-pixel庫獲取的內容
//level是灰度,用來與一個像素點rgb三個值加起來的數比較,用於確定該點的黑色程度,level越小表示越黑。
function getBoundary(pixels, level){
var lastY=0, count=0;
for(var i=0; i < pixels.shape[0]; i++) {
for(var j=0; j < pixels.shape[1]; j++) {
var rgb = pixels.get(i,j,0) + pixels.get(i,j,1) + pixels.get(i,j,2)
if(rgb < level && lastY+1 == j) {
count++;
//console.log(rgb, i,j ,count);
}
else count = 0;
lastY = j;
if(count > 6) return i;
}
}
return 0;
}
//獲取灰度值
function getGrey(pixels){
var rgb = 0;
for(var i=0; i < pixels.shape[0]; i++) {
for(var j=0; j < pixels.shape[1]; j++) {
rgb += pixels.get(i,j,0) + pixels.get(i,j,1) + pixels.get(i,j,2)
}
}
return Math.floor(rgb/pixels.shape[0]/pixels.shape[1])
}
}()
使用方法
好像沒啥特殊的,直接node就可以。
需要用npm
安裝get-pixels
,ndarray
這兩個包,用來識別圖像的。
我用的是firefox,如果用chrocme或safari,需要自己去替換引用和下載驅動。
週末看了點node教程,講node適用於任務調度,強項不是計算,怪不得網上的驗證碼代碼大多都是python寫的,而且遇到問題nodejs+selenium相關的答案較少,官網的文檔又比較簡單,廢了好大的勁。