1. 首页
  2. 后端

java + opencv + selenium 破解滑动验证码

  java + opencv + selenium 破解滑动验证码

================================

本文柯苏远写于2024年6月11日

背景

公司运营同学需要用账户名和密码登录别人网站,并查询数据(零件数据),然后从数据中分析出哪些数据是可用的,然后将结果记录到我们系统中。

运营同学向公司领导反馈这种方式虽然不复杂,但是太繁琐了,每天需要处理成百上千个目标数据查询然后分析,太耗费人力了,他们需求是能不能把这部分频繁且繁琐的动作让开发同学自动化实现,由运营同学提供站点,账户密码,以及对应数据是否可用的规则。

注意

本篇文章不会贴详细代码,也没有selenium以及opencv的详细教程。

不适合小白(没有selenium使用经验的)看这篇文章学习,意义不大,浪费时间。

chrome浏览器对应的webdriver下载地址:Chrome for Testing availability (googlechromelabs.github.io)

selenium+java的快速入门:保姆级自动化测试教程(Selenium+java)_java自动化测试-CSDN博客

opencv下载和简单使用参考文章:【OpenCV入门教程Java版之一】安装OpenCV:OpenCV4.6.0+IDEA开发环境配置_java opencv-CSDN博客

调研阶段

初步方案

其实初步方案我们是想通过分析页面对应按钮触发的接口,然后记录这个接口,用后端发http请求的方式的。

这种方式从理论上是可行的,但是有几个难点:

  1. 接口要保持登录态去获取数据。
  2. 分析页面对应接口比较耗时,比如说:接口的请求头,接口的必要cookie。

最后也确实是舍弃了这种方案,原因就是由于分析浏览器页面接口太麻烦了。

selenium 方案

在这个需求之前,我是没有用过selenium的,只是听说过是个ui自动化测试工具。

我对selenium的理解就是:动态分析dom元素然后用代码的方式控制鼠标的点击以及键盘的输入。

可能这个理解比较片面,但是我觉得够用了。

开发方案设计

文字描述:

  1. 运营同学输入一个零件号,后台将这个零件号放入到队列(用数据库表做的队列)里。
  2. 开发一个selenium爬取目标数据相关信息的后台服务,每次读取队列里一个零件进行查询,将查询到的结果进行分析,判断这个零件是否可用,并写回上面那张队列表。
  3. 运营同学获取这个零件对应的是否可用的状态。

方案设计图:

selenium抓取数据.png

待查询零件对列表设计:

CREATE TABLE crawler_part
(
    id                 BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'Unique ID for each auto part',
    part_number        VARCHAR(255) NOT NULL COMMENT 'Part number of the auto part',
    status             INT          NOT NULL DEFAULT 0 COMMENT 'Status of the auto part crawl: 0-not start,1-starting 2-Success, 3-Failure',
    result             VARCHAR(2000) COMMENT 'result of the part crawler',
    create_time        TIMESTAMP             DEFAULT CURRENT_TIMESTAMP COMMENT 'Time when the record was created',
    update_time        TIMESTAMP             DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Time when the record was last updated',
    INDEX              idx_status (status) COMMENT 'Index for fast retrieval based on status'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Table for auto parts crawled by a crawler';

实现过程中遇到的难点

难点在于:登录的时候如何验证滑动验证码。

网上参考了很多文章,最后找到了是通过opencv解决的。

通过参考这篇文章让我解决了滑动验证码的问题:基于Java+selenium+opencv模拟网页滑动验证_java + selenium 图片滑块拖动验证-CSDN博客

有点搞,现在这篇文章看不了了,要vip了,咳咳,但是一个合格的码农……懂得都懂……

演示

由于是公司提供商的网站,所以这里就不用实战网址进行测试滑动验证码了。

而是用掘金网址来做验证。(已获取掘金官方同意才做的

2.png

(不得不说,掘金是真大气,哈哈

演示视频

java + opencv + selenium 破解滑动验证码

核心代码

计算滑块移动的距离:

/**
 * 获取滑块移动的距离
 * @param backgroudUrl 滑动背景图
 * @param slideUrl 小滑块
 * @param top 高度:小滑块上边到背景图上边的距离,单位是px
 * @return
 */
public static String getDistance(String backgroudUrl, String slideUrl, int top) {

    // 加载OpenCV本地库
    System.load(DLL_PATH);
    File bFile = new File(CommonConstant.OPENCV_PICS + "background.jpg");
    File sFile = new File(CommonConstant.OPENCV_PICS + "slideUrl.jpg");
    try {
        //将图片复制保存到指定路径
        FileUtils.copyURLToFile(new URL(backgroudUrl), bFile);
        FileUtils.copyURLToFile(new URL(slideUrl), sFile);

        BufferedImage bgBI = ImageIO.read(bFile);
        BufferedImage sBI = ImageIO.read(sFile);

        // 裁剪
        // 小滑块左边置顶,所以需要从滑块的右上角点开始截图,宽度为小滑块的,宽度是背景图减去小滑块的宽度。
        bgBI = bgBI.getSubimage(sBI.getWidth(), top, bgBI.getWidth() - sBI.getWidth(), sBI.getHeight());
        ImageIO.write(bgBI, "png", bFile);

        Mat s_mat = Imgcodecs.imread(sFile.getPath());
        Mat b_mat = Imgcodecs.imread(bFile.getPath());

        // 转灰度图像
        Mat s_newMat = new Mat();
        Imgproc.cvtColor(s_mat, s_newMat, Imgproc.COLOR_BGR2GRAY);

        // 二值化图像
        binaryZation(s_newMat);
        Imgcodecs.imwrite(sFile.getPath(), s_newMat);

        // 让两张图片进行比对
        int result_rows = b_mat.rows() - s_mat.rows() + 1;
        int result_cols = b_mat.cols() - s_mat.cols() + 1;
        Mat g_result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
        // 归一化平方差匹配法
        Imgproc.matchTemplate(b_mat, s_mat, g_result, Imgproc.TM_SQDIFF);
        // 归一化相关匹配法
        Core.normalize(g_result, g_result, 0, 1, Core.NORM_MINMAX, -1, new Mat());

        Point matchLocation = new Point();
        Core.MinMaxLocResult mmlr = Core.minMaxLoc(g_result);
        matchLocation = mmlr.maxLoc; // 此处使用maxLoc还是minLoc取决于使用的匹配算法
        Imgproc.rectangle(b_mat, matchLocation,
                new Point(matchLocation.x + s_mat.cols(), matchLocation.y + s_mat.rows()), new Scalar(0, 255, 0, 0));
        //返回值就是要移动的距离,在这里需要加上被裁剪掉的宽度再减去小滑块的宽度,最后乘上相应的比例。
        // 这个1.62不是固定的,但是对于juejin的验证码来说就是固定的
        // 公式:原图px / 掘金验证码显示的大小px = 1.62
        // 换了网站的话,用上面公式进行重新计算。
        return "" + ((matchLocation.x + s_mat.cols()) / 1.62);
    } catch (Throwable e) {
        e.printStackTrace();
        return null;
    } finally {
        //删除保存的滑块以及背景图片
        bFile.delete();
        sFile.delete();
    }
}

自己动手试试,学会selenium的简单语法之后就可以了,难点就是这个计算距离的方法。

总结

  1. 总结这次抓取数据的方案设计。
  2. selenium我觉得使用过程中没有什么难点,主要是业务上的难点,比如说完成滑动验证码的认证。
  3. opencv的简单使用。
    原文链接: https://juejin.cn/post/7379121513419112485

文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17727.html

QR code