极验滑动验证码识别


本文将展示如何使用Rust语言实现极验滑动验证码的自动识别。从模拟点击到识别滑动缺口、计算位移并模拟拖动滑块。如果认证失败,则重复调用直到成功。

识别思路
模拟点击切换为滑动验证,并显示验证界面。
识别滑动缺口的位置,计算位移。
模拟拖动滑块。
若认证失败,重复调用。
详细过程及代码
初始化
首先,初始化Selenium对象和一些参数配置,极验验证码测试页面的网址如下:

rust

extern crate webdriver;
extern crate image;

use webdriver::client::sync::{Client, WebDriver};
use std::fs::File;
use std::time::Duration;
use std::thread::sleep;

const BORDER: i32 = 6;

struct CrackGeetest {
    url: String,
    client: Client,
}

impl CrackGeetest {
    fn new() -> CrackGeetest {
        let client = Client::new("http://localhost:4444").unwrap();
        CrackGeetest {
            url: String::from("https://www.geetest.com/type/"),
            client,
        }
    }

    fn open(&mut self) {
        self.client.goto(&self.url).unwrap();
    }

    fn close(&mut self) {
        self.client.close().unwrap();
    }
}
定义了一个 CrackGeetest 结构体,初始化Selenium对象和一些参数配置,网址是极验的验证码测试页面。

模拟点击
首先模拟点击切换为滑动验证,然后模拟点击弹出验证图片。

rust

impl CrackGeetest {
    fn change_to_slide(&self) {
        self.client.find(Locator::Css(".products-content ul > li:nth-child(2)")).unwrap().click().unwrap();
    }

    fn get_geetest_button(&self) {
        self.client.find(Locator::Css(".geetest_radar_tip")).unwrap().click().unwrap();
    }
}
该步骤定义了两个方法,均利用显示等待的方法实现。并返回按钮对象,后用click方法模拟点击。

获取背景图
首先等待验证码加载完成(wait_pic),获取网页截图(get_screenshot),然后获取验证背景图所在的位置及大小参数(get_position)和滑块对象(get_slider)。

rust

impl CrackGeetest {
    fn wait_pic(&self) {
        self.client.find(Locator::Css(".geetest_popup_wrap")).unwrap();
    }

    fn get_screenshot(&self) -> image::DynamicImage {
        let screenshot = self.client.screenshot().unwrap();
        image::load_from_memory(&screenshot).unwrap()
    }

    fn get_position(&self) -> (u32, u32, u32, u32) {
        let img = self.client.find(Locator::Css(".geetest_canvas_img")).unwrap();
        sleep(Duration::from_secs(2));
        let location = img.location().unwrap();
        let size = img.size().unwrap();
        (
            location.y as u32,
            (location.y + size.height) as u32,
            location.x as u32,
            (location.x + size.width) as u32,
        )
    }

    fn get_slider(&self) {
        self.client.find(Locator::Css(".geetest_slider_button")).unwrap();
    }
}
再通过上述返回的背景图位置和大小参数,对网页截图进行切片(get_geetest_image),最后获取背景图。

rust

impl CrackGeetest {
    fn get_geetest_image(&self, name: &str) -> image::DynamicImage {
        let (top, bottom, left, right) = self.get_position();
        println!("验证码位置: {} {} {} {}", top, bottom, left, right);
        let screenshot = self.get_screenshot();
        let captcha = screenshot.crop(left, top, right - left, bottom - top);
        let mut output = File::create(name).unwrap();
        captcha.write_to(&mut output, image::ImageOutputFormat::Png).unwrap();
        captcha
    }
}
到这里,已经获取了带缺口的背景图。我们需要获取不带缺口滑块的原图。这里通过改变CSS样式获得原图。

rust

impl CrackGeetest {
    fn delete_style(&self) {
        self.client.execute("document.querySelectorAll('canvas')[2].style=''", vec![]).unwrap();
    }
}
执行js脚本之后(delete_style)获得了无缺口的原图,再调用之前的截图方法,就可以获取同大小的背景图。

识别缺口
我们得到了两张图,接下来对比它们来获取缺口位置。更多内容联系1436423940

rust

fn is_pixel_equal(img1: &image::DynamicImage, img2: &image::DynamicImage, x: u32, y: u32) -> bool {
    let pixel1 = img1.get_pixel(x, y).0;
    let pixel2 = img2.get_pixel(x, y).0;
    let threshold = 60;
    (pixel1[0] as i32 - pixel2[0] as i32).abs() < threshold
        && (pixel1[1] as i32 - pixel2[1] as i32).abs() < threshold
        && (pixel1[2] as i32 - pixel2[2] as i32).abs() < threshold
}
get_gap()方法遍历两张图片的每个像素,再利用is_pixel_equal()方法判断两张图片同一位置的像素。

rust

fn get_gap(img1: &image::DynamicImage, img2: &image::DynamicImage) -> u32 {
    let mut left = 60;
    for i in left..img1.width() {
        for j in 0..img1.height() {
            if !is_pixel_equal(img1, img2, i, j) {
                left = i;
                return left;
            }
        }
    }
    left
}
模拟拖动
我们获得了滑块的位置,现在只需计算距离并模拟拖动即可。

rust

fn get_track(distance: i32) -> Vec<i32> {
    let mut track = Vec::new();
    let mut current = 0;
    let mid = distance * 3 / 5;
    let t = 0.2;
    let mut v = 0.0;
    let distance = distance + 14;
    while current < distance {
        let a = if current < mid { 2.0 } else { -1.5 };
        let v0 = v;
        v = v0 + a * t;
        let move_distance = v0 * t + 0.5 * a * t * t;
        current += move_distance as i32;
        track.push(move_distance as i32);
    }
    track
}
前3/5路程加速,后面减速,track返回的是一个列表,其中每个元素代表的是每次移动的距离。然后模拟释放鼠标时的人手抖动(shake_mouse)。

rust

fn shake_mouse(client: &Client) {
    client.perform_actions().unwrap();
}

fn move_to_gap(client: &Client, tracks: Vec<i32>) {
    let back_tracks = vec![-1, -1, -2, -2, -3, -2, -2, -1, -1];
    client.perform_actions().unwrap();
    for &x in &tracks {
        client.mouse_move_by(x, 0).unwrap();
    }
    for &x in &back_tracks {
        client.mouse_move_by(x, 0).unwrap();
    }
    shake_mouse(client);
    sleep(Duration::from_millis(500));
    client.perform_actions().unwrap();
}
最后根据之前所得到的运动轨迹拖动滑块(move_to_gap)即可。

整个控制流程
执行主体流程,若验证失败,则再次调用crack()进行识别,直至成功。

rust

impl CrackGeetest {
    fn crack(&mut self) {
        loop {
            self.open();
            self.change_to_slide();
            self.get_geetest_button();
            self.wait_pic();
            self.get_slider();
            let image1 = self.get_geetest_image("captcha1.png");
            self.delete_style();
            let image2 = self.get_geetest_image("captcha2.png");
            let gap = get_gap(&image1, &image2);
            println!("缺口位置: {}", gap);
            let gap = gap as i32 - BORDER;
            let track = get_track(gap);
            move_to_gap(&self.client, track);
            let success = self.client.find(Locator::Css(".geetest_success_radar_tip_content")).unwrap().text().unwrap() == "验证成功";
            println!("{}", success);
            if success {
                break;
            }
            sleep(Duration::from_secs(5));
            self.close();
        }
    }
}

fn main() {
    let mut crack = CrackGeetest::new();
    crack.crack();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值