本文将介绍如何使用D语言和Selenium WebDriver来破解极验滑动验证码。我们将详细介绍每一步的实现过程,包括模拟点击、识别滑动缺口、计算位移以及模拟拖动滑块。
识别流程
打开网页并切换为滑动验证模式。
识别滑动缺口的位置,计算位移。
模拟拖动滑块。
若认证失败,重复调用。
实现步骤与代码
初始化
首先,初始化Selenium WebDriver对象并配置参数。极验验证码测试页面的网址如下:
d
import std.stdio;
import std.net.curl;
import std.json;
import std.conv;
import std.algorithm;
import std.range;
import std.string;
import std.exception;
import std.array;
import std.file;
import std.process;
import std.parallelism;
import selenium.webdriver;
import selenium.elements;
import selenium.common.by;
import selenium.support.ui;
import core.time;
enum URL = "https://www.geetest.com/type/";
enum BORDER = 6;
class CrackGeetest {
WebDriver driver;
WebDriverWait wait;
this() {
driver = new ChromeDriver();
wait = new WebDriverWait(driver, 10.seconds);
}
void open() {
driver.get(URL);
}
void close() {
driver.quit();
}
WebElement changeToSlide() { 更多内容联系1436423940
return wait.until(elementToBeClickable(By.cssSelector(".products-content ul > li:nth-child(2)")));
}
WebElement getGeetestButton() {
return wait.until(elementToBeClickable(By.cssSelector(".geetest_radar_tip")));
}
void waitPic() {
wait.until(presenceOfElementLocated(By.cssSelector(".geetest_popup_wrap")));
}
string getScreenshot() {
string screenshot = driver.getScreenshotAs(Base64);
return screenshot;
}
(int, int, int, int) getPosition() {
auto img = wait.until(presenceOfElementLocated(By.className("geetest_canvas_img")));
Thread.sleep(2.seconds);
auto location = img.location;
auto size = img.size;
return (location.y, location.y + size.height, location.x, location.x + size.width);
}
WebElement getSlider() {
return wait.until(elementToBeClickable(By.className("geetest_slider_button")));
}
void saveImage(string base64, string fileName) {
auto bytes = base64.decodeBase64;
write(fileName, bytes);
}
void getGeetestImage(string name) {
auto (top, bottom, left, right) = getPosition();
writeln("验证码位置: ", top, bottom, left, right);
auto screenshot = getScreenshot();
saveImage(screenshot, name);
}
void deleteStyle() {
driver.executeScript("document.querySelectorAll('canvas')[2].style=''");
}
bool isPixelEqual(string img1, string img2, int x, int y) {
auto cmd = `compare -metric AE -subimage-search -dissimilarity-threshold 0.1 ` ~ img1 ~ ` ` ~ img2 ~ ` diff.png`;
auto result = executeShell(cmd);
return result.status == 0;
}
int getGap(string img1, string img2) {
int left = 60;
foreach (i; left .. img1.length) {
foreach (j; 0 .. img1.length) {
if (!isPixelEqual(img1, img2, i, j)) {
return i;
}
}
}
return left;
}
int[] getTrack(int distance) {
int[] track;
int current = 0;
int mid = distance * 3 / 5;
double t = 0.2;
int v = 0;
distance += 14;
while (current < distance) {
int a = (current < mid) ? 2 : -1.5;
int v0 = v;
v = v0 + cast(int)(a * t);
int move = cast(int)(v0 * t + 0.5 * a * t * t);
current += move;
track ~= move;
}
return track;
}
void moveToGap(WebElement slider, int[] tracks) {
driver.actions().clickAndHold(slider).perform();
foreach (move; tracks) {
driver.actions().moveByOffset(move, 0).perform();
}
foreach (move; [-1, -1, -2, -2, -3, -2, -2, -1, -1]) {
driver.actions().moveByOffset(move, 0).perform();
}
shakeMouse();
Thread.sleep(500.msecs);
driver.actions().release().perform();
}
void shakeMouse() {
driver.actions().moveByOffset(-3, 0).perform();
driver.actions().moveByOffset(2, 0).perform();
}
void crack() {
open();
changeToSlide().click();
getGeetestButton().click();
waitPic();
auto slider = getSlider();
getGeetestImage("captcha1.png");
deleteStyle();
getGeetestImage("captcha2.png");
int gap = getGap("captcha1.png", "captcha2.png") - BORDER;
auto track = getTrack(gap);
moveToGap(slider, track);
auto success = wait.until(textToBePresentInElementLocated(By.className("geetest_success_radar_tip_content"), "验证成功"));
if (success) {
writeln("验证成功");
} else {
writeln("验证失败,重试中...");
crack();
}
close();
}
}
void main() {
auto crack = new CrackGeetest();
crack.crack();
}