准备工作
安装Rust和相关库
首先,你需要安装Rust编译器。如果你还没有安装,可以访问Rust官网进行安装。
接下来,我们需要安装Rust的Selenium客户端库fantoccini和OpenCV的Rust绑定库opencv。
在Cargo.toml中添加依赖:
[dependencies]
fantoccini = "0.15"
opencv = { version = "0.52", features = ["contrib", "opencv-4"] }
验证码页面
验证码页面地址:https://dun.163.com/trial/jigsaw
代码实现
1. 初始化Selenium
我们首先启动Selenium WebDriver并导航到验证码页面。
rust
复制代码
use fantoccini::{Client, Locator};
use std::time::Duration;
use tokio;
#[tokio::main]
async fn main() -> Result<(), fantoccini::error::CmdError> {
let mut caps = serde_json::map::Map::new();
caps.insert("browserName".to_string(), serde_json::Value::String("chrome".to_string()));
let c = Client::with_capabilities("http://localhost:4444", caps).await?;
c.goto("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(Duration::from_secs(3)).await;
// 后续的验证码处理代码将放在这里
c.close().await
}
2. 获取验证码图片
获取验证码图片并保存到本地,以便使用OpenCV进行图像处理。
rust
use std::fs::File;
use std::io::Write;
async fn get_image(c: &Client, selector: Locator, file_path: &str) -> Result<(), fantoccini::error::CmdError> {
let elem = c.find(selector).await?;
let img_url = elem.attr("src").await?.unwrap();
let resp = reqwest::get(&img_url).await?.bytes().await?;
let mut file = File::create(file_path)?;
file.write_all(&resp)?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut caps = serde_json::map::Map::new();
caps.insert("browserName".to_string(), serde_json::Value::String("chrome".to_string()));
let c = Client::with_capabilities("http://localhost:4444", caps).await?;
c.goto("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(Duration::from_secs(3)).await;
get_image(&c, Locator::Css(".yidun_bg-img"), "bg.png").await?;
get_image(&c, Locator::Css(".yidun_jigsaw"), "puzzle.png").await?;
// 后续的验证码处理代码将放在这里
c.close().await?;
Ok(())
}
3. 使用OpenCV匹配图像
使用OpenCV找到拼图缺口的位置。
rust
use opencv::{
core::{Point, Scalar, Size},
imgcodecs::{imread, imwrite, IMREAD_COLOR},
imgproc::{match_template, TM_CCOEFF_NORMED},
prelude::*,
};
fn get_slide_distance() -> Result<f64, Box<dyn std::error::Error>> {
let bg_image = imread("bg.png", IMREAD_COLOR)?;
if bg_image.empty() {
return Err("Error reading background image".into());
}
let puzzle_image = imread("puzzle.png", IMREAD_COLOR)?;
if puzzle_image.empty() {
return Err("Error reading puzzle image".into());
}
let mut result = Mat::default();
match_template(&bg_image, &puzzle_image, &mut result, TM_CCOEFF_NORMED, &Mat::default())?;
let min_max_loc = core::min_max_loc(&result, None, None, None, None)?;
let match_location = min_max_loc.max_loc;
Ok(match_location.x)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut caps = serde_json::map::Map::new();
caps.insert("browserName".to_string(), serde_json::Value::String("chrome".to_string()));
let c = Client::with_capabilities("http://localhost:4444", caps).await?;
c.goto("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(Duration::from_secs(3)).await;
get_image(&c, Locator::Css(".yidun_bg-img"), "bg.png").await?;
get_image(&c, Locator::Css(".yidun_jigsaw"), "puzzle.png").await?;
let distance = get_slide_distance()?;
println!("Slide distance: {}", distance);
// 后续的验证码处理代码将放在这里
c.close().await?;
Ok(())
}
4. 模拟滑动行为
模拟人类滑动行为,以避免被检测为机器人。
rust
use rand::Rng;
async fn slide_puzzle(c: &Client, distance: f64) -> Result<(), fantoccini::error::CmdError> {
let slider = c.find(Locator::Css(".yidun_slider")).await?;
let rect = slider.rect().await?;
let start_x = rect.x + rect.width / 2;
let start_y = rect.y + rect.height / 2;
let mut actions = c.action_chain();
actions = actions.move_to_element_center(&slider).click_and_hold();
let steps = 30;
let move_x = distance / steps as f64;
for i in 0..steps {
let current_x = start_x + (move_x * i as f64) as i32;
actions = actions.move_by_offset(current_x - start_x, 0);
tokio::time::sleep(Duration::from_millis(100 + rand::thread_rng().gen_range(0..50))).await;
}
actions.release().perform().await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut caps = serde_json::map::Map::new();
caps.insert("browserName".to_string(), serde_json::Value::String("chrome".to_string()));
let c = Client::with_capabilities("http://localhost:4444", caps).await?;
c.goto("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(Duration::from_secs(3)).await;
get_image(&c, Locator::Css(".yidun_bg-img"), "bg.png").await?;
get_image(&c, Locator::Css(".yidun_jigsaw"), "puzzle.png").await?;
let distance = get_slide_distance()?;
println!("Slide distance: {}", distance);
slide_puzzle(&c, distance).await?;
tokio::time::sleep(Duration::from_secs(5)).await;
c.close().await?;
Ok(())
}