本文将介绍如何使用TypeScript和Selenium WebDriver来实现极验滑动验证码的自动识别与破解。我们将详细讲解每一步的实现,包括模拟点击、识别滑动缺口、计算位移以及模拟拖动滑块。
识别思路
模拟点击切换为滑动验证,并显示验证界面。
识别滑动缺口的位置,计算位移。
模拟拖动滑块。
若认证失败,重复调用。
实现步骤与代码
初始化
首先,初始化Selenium WebDriver对象并配置参数。极验验证码测试页面的网址如下:
typescript
import { Builder, By, until, WebDriver } from 'selenium-webdriver';
import { promises as fs } from 'fs';
import Jimp from 'jimp';
const BORDER = 6;
const URL = 'https://www.geetest.com/type/';
class CrackGeetest {
private driver: WebDriver;
constructor() {
this.driver = new Builder().forBrowser('chrome').build();
}
async open() {
await this.driver.get(URL);
}
async close() {
await this.driver.quit();
}
async changeToSlide() {
const slideOption = await this.driver.wait(until.elementLocated(By.css('.products-content ul > li:nth-child(2)')), 10000);
await slideOption.click();
}
async getGeetestButton() {
const button = await this.driver.wait(until.elementLocated(By.css('.geetest_radar_tip')), 10000);
await button.click();
}
async waitPic() {
await this.driver.wait(until.elementLocated(By.css('.geetest_popup_wrap')), 10000);
}
async getScreenshot(): Promise<Buffer> {
const screenshot = await this.driver.takeScreenshot();
return Buffer.from(screenshot, 'base64');
}
async getPosition() {
const img = await this.driver.findElement(By.className('geetest_canvas_img'));
const location = await img.getRect();
return location;
}
async getSlider() {
const slider = await this.driver.wait(until.elementLocated(By.className('geetest_slider_button')), 10000);
return slider;
}
async getGeetestImage(filename: string) {
const { x, y, width, height } = await this.getPosition();
const screenshot = await this.getScreenshot();
const image = await Jimp.read(screenshot);
image.crop(x, y, width, height);
await image.writeAsync(filename);
return image;
}
async deleteStyle() {
await this.driver.executeScript('document.querySelectorAll("canvas")[2].style=""');
}
isPixelEqual(img1: Jimp, img2: Jimp, x: number, y: number): boolean {
const threshold = 60;
const pixel1 = Jimp.intToRGBA(img1.getPixelColor(x, y));
const pixel2 = Jimp.intToRGBA(img2.getPixelColor(x, y));
return Math.abs(pixel1.r - pixel2.r) < threshold &&
Math.abs(pixel1.g - pixel2.g) < threshold &&
Math.abs(pixel1.b - pixel2.b) < threshold;
}
async getGap(img1: Jimp, img2: Jimp): Promise<number> {
const left = 60;
for (let i = left; i < img1.bitmap.width; i++) {
for (let j = 0; j < img1.bitmap.height; j++) {
if (!this.isPixelEqual(img1, img2, i, j)) {
return i;
}
}
}
return left;
}
getTrack(distance: number): number[] {
const track = [];
let current = 0;
const mid = distance * 3 / 5;
const t = 0.2;
let v = 0;
distance += 14;
while (current < distance) {
let a;
if (current < mid) {
a = 2;
} else {
a = -1.5;
}
const v0 = v;
v = v0 + a * t;
const move = v0 * t + 0.5 * a * t * t;
current += move;
track.push(Math.round(move));
}
return track;
}
async moveToGap(slider: WebDriver, tracks: number[]) {
await this.driver.actions().clickAndHold(slider).perform();
for (const move of tracks) {
await this.driver.actions().move({ x: move, y: 0 }).perform();
}
const backTracks = [-1, -1, -2, -2, -3, -2, -2, -1, -1];
for (const move of backTracks) {
await this.driver.actions().move({ x: move, y: 0 }).perform();
}
await this.shakeMouse();
await this.driver.actions().release().perform();更多内容联系1436423940
}
async shakeMouse() {
await this.driver.actions().move({ x: -3, y: 0 }).perform();
await this.driver.actions().move({ x: 2, y: 0 }).perform();
}
async crack() {
try {
await this.open();
await this.changeToSlide();
await this.getGeetestButton();
await this.waitPic();
const slider = await this.getSlider();
const image1 = await this.getGeetestImage('captcha1.png');
await this.deleteStyle();
const image2 = await this.getGeetestImage('captcha2.png');
const gap = (await this.getGap(image1, image2)) - BORDER;
const track = this.getTrack(gap);
await this.moveToGap(slider, track);
const success = await this.driver.wait(until.elementTextContains(await this.driver.findElement(By.className('geetest_success_radar_tip_content')), '验证成功'), 10000);
console.log(success);
} catch (error) {
console.error('Failed-Retry', error);
await this.crack();
} finally {
await this.close();
}
}
}
(async () => {
const crack = new CrackGeetest();
await crack.crack();
})();