基於nodejs+selenium自動過滑動驗證碼的QQ刷贊

引子

不知道大家是否還記得之前讓我刷讚的小朋友,自從他回到學校後,可能是由於學業太忙,把我們組成刷贊團隊的事情忘記了,只是每天早上給我發信息幫他刷贊,遲遲沒有新的用戶,刷贊就沒有量產的動力,我怠慢了幾天沒給他刷,他就不再理我了,我覺得是中了他的圈套,他只是嘴上說說要發展團隊,其實只是貪戀我每天給他刷幾萬個贊。

效果

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-pixelsndarray這兩個包,用來識別圖像的。
我用的是firefox,如果用chrocme或safari,需要自己去替換引用和下載驅動。

週末看了點node教程,講node適用於任務調度,強項不是計算,怪不得網上的驗證碼代碼大多都是python寫的,而且遇到問題nodejs+selenium相關的答案較少,官網的文檔又比較簡單,廢了好大的勁。

發佈了39 篇原創文章 · 獲贊 37 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章