准备工作
安装Rust和相关库
首先,你需要安装Rust编译器和包管理工具Cargo。如果你还没有安装,可以访问 Rust官网 进行安装。
接下来,我们需要安装chromiumoxide库和OpenCV的Rust绑定opencv库。
在项目目录下创建一个新的Rust项目:
sh
cargo new captcha_solver
cd captcha_solver
然后,在Cargo.toml文件中添加以下依赖:
toml
[dependencies]
chromiumoxide = "0.2"
opencv = "0.54"
tokio = { version = "1", features = ["full"] }
验证码页面
验证码页面地址:https://dun.163.com/trial/jigsaw
代码实现
1. 初始化Chromiumoxide
我们首先启动Chromiumoxide并导航到验证码页面。
rust
use chromiumoxide::browser::{Browser, BrowserConfig};
use chromiumoxide::cdp::browser_protocol::page::ScreenshotFormat;
use chromiumoxide::cdp::browser_protocol::page::Viewport;
use chromiumoxide::Page;
use tokio;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (browser, mut handler) = Browser::launch(
BrowserConfig::builder().build()?
).await?;
let page = browser.new_page("https://dun.163.com/trial/jigsaw").await?;
// 等待页面加载完成
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
// 后续的验证码处理代码将放在这里
Ok(())
}
2. 获取验证码图片
获取验证码图片并保存到本地,以便使用OpenCV进行图像处理。
rust
use std::fs::File;
use std::io::Write;
async fn get_image(page: &Page, selector: &str, file_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let js_code = format!(r#"
document.querySelector("{}").src;
"#, selector);
let image_url: String = page.evaluate(js_code).await?.into_value()?;
let response = reqwest::get(&image_url).await?;
let mut file = File::create(file_path)?;
let content = response.bytes().await?;
file.write_all(&content)?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (browser, mut handler) = Browser::launch(
BrowserConfig::builder().build()?
).await?;
let page = browser.new_page("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
get_image(&page, ".yidun_bg-img", "bg.png").await?;
get_image(&page, ".yidun_jigsaw", "puzzle.png").await?;
// 后续的验证码处理代码将放在这里
Ok(())
}
3. 使用OpenCV匹配图像
使用OpenCV找到拼图缺口的位置。
rust
use opencv::core::{Mat, Point, Scalar, Size, CV_8UC1, CV_32FC1};
use opencv::imgcodecs::{imread, IMREAD_COLOR};
use opencv::imgproc::{cvt_color, match_template, min_max_loc, COLOR_BGR2GRAY, TM_CCOEFF_NORMED};
fn get_slide_distance() -> Result<i32, Box<dyn std::error::Error>> {
let bg_image = imread("bg.png", IMREAD_COLOR)?;
let puzzle_image = imread("puzzle.png", IMREAD_COLOR)?;
let mut gray_bg = Mat::default()?;
cvt_color(&bg_image, &mut gray_bg, COLOR_BGR2GRAY, 0)?;
let mut gray_puzzle = Mat::default()?;
cvt_color(&puzzle_image, &mut gray_puzzle, COLOR_BGR2GRAY, 0)?;
let mut result = Mat::new_size(gray_bg.size()?, CV_32FC1)?;
match_template(&gray_bg, &gray_puzzle, &mut result, TM_CCOEFF_NORMED, &Mat::default()?)?;
let mut min_val = 0.0;
let mut max_val = 0.0;
let mut min_loc = Point::default();
let mut max_loc = Point::default();
min_max_loc(&result, Some(&mut min_val), Some(&mut max_val), Some(&mut min_loc), Some(&mut max_loc), &Mat::default()?)?;
Ok(max_loc.x)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (browser, mut handler) = Browser::launch(
BrowserConfig::builder().build()?
).await?;
let page = browser.new_page("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
get_image(&page, ".yidun_bg-img", "bg.png").await?;
get_image(&page, ".yidun_jigsaw", "puzzle.png").await?;
let distance = get_slide_distance()?;
println!("Slide distance: {}", distance);
// 后续的验证码处理代码将放在这里
Ok(())
}
4. 模拟滑动行为
模拟人类滑动行为,以避免被检测为机器人。
rust
use chromiumoxide::cdp::browser_protocol::input::{dispatch_mouse_event, MouseButton};
async fn slide_puzzle(page: &Page, distance: i32) -> Result<(), Box<dyn std::error::Error>> {
let slider = page.find_element(".yidun_slider").await?;
let bounding_box = slider.bounding_box().await?.unwrap();
let start_x = bounding_box.x + bounding_box.width / 2.0;
let start_y = bounding_box.y + bounding_box.height / 2.0;
page.dispatch_mouse_event(dispatch_mouse_event::Type::MouseMoved, start_x, start_y).await?;
page.dispatch_mouse_event(dispatch_mouse_event::Type::MousePressed, start_x, start_y)
.button(MouseButton::Left)
.click_count(1)
.await?;
let steps = 30;
let move_x = distance as f64 / steps as f64;
for i in 0..steps {
let current_x = start_x + i as f64 * move_x;
page.dispatch_mouse_event(dispatch_mouse_event::Type::MouseMoved, current_x, start_y).await?;
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
page.dispatch_mouse_event(dispatch_mouse_event::Type::MouseReleased, start_x + distance as f64, start_y)
.button(MouseButton::Left)
.click_count(1)
.await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { 更多内容联系1436423940
let (browser, mut handler) = Browser::launch(
BrowserConfig::builder().build()?
).await?;
let page = browser.new_page("https://dun.163.com/trial/jigsaw").await?;
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
get_image(&page, ".yidun_bg-img", "bg.png").await?;
get_image(&page, ".yidun_jigsaw", "puzzle.png").await?;
let distance = get_slide_distance()?;
println!("Slide distance: {}", distance);
slide_puzzle(&page, distance).await?;
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
Ok(())
}