当前位置:   article > 正文

自动化-滑块验证码 Java + Selenium + OpenCV_java 实现自动滑块

java 实现自动滑块

目录


前言

  最近我在工作中接到了一个任务,需要编写一个网页自动化脚本。说实话,这是我第一次尝试编写这样的脚本。一些普通的操作,对我来说还算简单,能用Selenium来解决。但在对滑块验证码进行自动化时,只用Selenium已经无法满足,最终结合了OpenCV,才解决了这个难题。自动化滑块验证的解决方案,网上有很多,但用Java来编程的很少,值得一记!

一、配置项目环境

1.安装和引入OpenCV库

直接参考我的另一篇文章即可,里面写的非常详细:在Java中使用OpenCV-CSDN博客

2.引入Selenium库

这里我们使用Maven导入,代码如下:

  1. <dependency>
  2. <groupId>org.seleniumhq.selenium</groupId>
  3. <artifactId>selenium-java</artifactId>
  4. <version>4.21.0</version>
  5. </dependency>

3.下载对应浏览器驱动

Web自动化需要用到浏览器驱动,我使用的是谷歌浏览器,因此需要下载谷歌浏览器的驱动程序。先查询浏览器的内核版本,知道内核版本后,就可以下载对应版本的内核了。

下载解压之后会得到一个chromedriver.exe,这就是我们需要用到的驱动程序。

下载地址:dreamshao/chromedriver (github.com)

下载地址:chromedriver.storage.googleapis.com/index.html(旧版本)

操作如下图:

二、开始编码

1.OpenCV部分-关建编码

做滑块验证自动化,最关键就是位置信息,只要我们能获取到相关的位置信息,我们就能使用selenium来将滑块移动到对应位置。为了获取这一位置,我们需要用到OpenCV,来对图片进行操作。原理便是比较背景图和滑块图的轮廓,找出相似的地方并获取坐标

在编写OpenCV相关代码前,我们需要加载OpenCV库。

  1. //因为我们前面已经配置好,这里可以直接加载库文件
  2. System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

 加载后,我们需要获取到滑块图已经背景图的图片,这一部分可以用selenium来操作。后面会封装成一个函数,所以这里暂时不管。上OpenCV代码!

读取背景图和滑块图

  1. //背景图地址
  2. String bgPath = "path/path/XXX.jpg";
  3. //滑块图地址
  4. String sdrPath = "path/path/XXX/jpg";
  5. //使用Mat类分别读取背景图和滑块图
  6. Mat bg = Imgcodecs.imread(bgPath);
  7. Mat slider = Imgcodecs.imread(sdrPath);

 将背景图和滑块图转换为灰度图 —— 重要步骤

  1. // 新建Mat类,用于存放灰度图
  2. Mat bg_gray = new Mat();
  3. Mat sdr_gray = new Mat();
  4. //将背景图转换为灰度图
  5. Imgproc.cvtColor(bg, bg_gray, Imgproc.COLOR_BGR2GRAY);
  6. //将滑块图转换为灰度图
  7. Imgproc.cvtColor(slider, sdr_gray, Imgproc.COLOR_BGR2GRAY);
  8. //保存灰度图
  9. Imgcodecs.imwrite(".\\bg_gray.png", bg_gray);
  10. Imgcodecs.imwrite(".\\sdr_gray.png", sdr_gray);

在将灰度图转化为轮廓图 —— 重要步骤 

  1. //新建两个Mat类来存放轮廓图
  2. Mat bg_edg = new Mat();
  3. Mat sdr_edg = new Mat();
  4. //转成轮廓图。后面两个数值是阈值,阈值可能得调整,调到轮廓清晰即可
  5. Imgproc.Canny(bg_gray, bg_edg, 20, 200);
  6. Imgproc.Canny(sdr_gray, sdr_edg, 20, 200);
  7. //保存轮廓图
  8. Imgcodecs.imwrite(".\\bg_edg.png", bg_edg);
  9. Imgcodecs.imwrite(".\\sdr_edg.png", sdr_edg);

 上面两个步骤很重要,如果不做,匹配出来得结果会不准确。代码运行后效果如下:

背景图-原图
滑块图-原图

滑块图-灰度图
背景图-灰度图

背景图-轮廓图
滑块图-轮廓图

 对轮廓图进行比对,筛选图轮廓相似的地方,并同方框标出来

  1. //匹配轮廓,并将结果保存到res_img
  2. Mat res_img = new Mat();
  3. Imgproc.matchTemplate(bg_edg, sdr_edg, res_img, Imgproc.TM_CCOEFF_NORMED);
  4. //获取匹配最大和最小的结果
  5. Core.MinMaxLocResult minMaxLoc = Core.minMaxLoc(res_img);
  6. //获取最小的匹配度
  7. double minVal = minMaxLoc.minVal;
  8. //获取最大的匹配度
  9. double maxVal = minMaxLoc.maxVal;
  10. //获取最小匹配度的坐标
  11. Point minLoc = minMaxLoc.minLoc;
  12. //获取最大匹配度的坐标
  13. Point maxLoc = minMaxLoc.maxLoc;
  14. // 获取模板图片(即滑块图)的宽度和高度
  15. int tw = sdr_edg.cols();
  16. int th = sdr_edg.rows();
  17. // 左上角点的坐标
  18. Point tl = maxLoc;
  19. // 计算右下角点的坐标
  20. Point br = new Point(tl.x + tw, tl.y + th);
  21. // 在原背景图上绘制矩形
  22. Imgproc.rectangle(bg, tl, br, new Scalar(0, 0, 255), 2);
  23. // 保存图片
  24. Imgcodecs.imwrite(".\\result.png", bg);

 运行结果如下:

 

非常完美!缺口位置准确,现在只需要一些数学运算,我们就获取到缺口偏移量。

接下来便可以封装成一个函数了(注意图片的读取方式,例子是从http上读取图片),

代码如下:

  1. public double getSilderOffset(String bgPath, String sdrPath) {
  2. System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  3. //读取图片需要具体情况具体分析,这里的例子是从http上读取图片
  4. //读取背景图
  5. Mat bg = loadImageFromURL(bgPath);
  6. //读取滑块图
  7. Mat slider = loadImageFromURL(sdrPath);
  8. // 检查图片是否成功读取
  9. if (bg.empty()|| slider.empty()) {
  10. System.out.println("Error: Image cannot be loaded!");
  11. return 0;
  12. }
  13. //
  14. // 转换为灰度图像
  15. Mat bg_gray = new Mat();
  16. Mat sdr_gray = new Mat();
  17. Imgproc.cvtColor(bg, bg_gray, Imgproc.COLOR_BGR2GRAY);
  18. Imgproc.cvtColor(slider, sdr_gray, Imgproc.COLOR_BGR2GRAY);
  19. //保存灰度图
  20. // Imgcodecs.imwrite(".\\bg_gray.png", bg_gray);
  21. // Imgcodecs.imwrite(".\\sdr_gray.png", sdr_gray);
  22. //转换为轮廓图
  23. Mat bg_edg = new Mat();
  24. Mat sdr_edg = new Mat();
  25. Imgproc.Canny(bg_gray, bg_edg, 20, 200); //后面两个数值是阈值,阈值可能得调整,调到轮廓清晰即可
  26. Imgproc.Canny(sdr_gray, sdr_edg, 20, 200);
  27. //保存轮廓图
  28. // Imgcodecs.imwrite(".\\bg_edg.png", bg_edg);
  29. // Imgcodecs.imwrite(".\\sdr_edg.png", sdr_edg);
  30. //模板匹配
  31. Mat res_img = new Mat();
  32. Imgproc.matchTemplate(bg_edg, sdr_edg, res_img, Imgproc.TM_CCOEFF_NORMED);
  33. //获取匹配最大和最小的结果
  34. Core.MinMaxLocResult minMaxLoc = Core.minMaxLoc(res_img);
  35. //获取最小的匹配度
  36. double minVal = minMaxLoc.minVal;
  37. //获取最大的匹配度
  38. double maxVal = minMaxLoc.maxVal;
  39. //获取最小匹配度的坐标
  40. Point minLoc = minMaxLoc.minLoc;
  41. //获取最大匹配度的坐标
  42. Point maxLoc = minMaxLoc.maxLoc;
  43. // 获取模板图片(即滑块图)的宽度和高度
  44. int tw = sdr_edg.cols();
  45. int th = sdr_edg.rows();
  46. // 左上角点的坐标
  47. Point tl = maxLoc;
  48. // 计算右下角点的坐标
  49. Point br = new Point(tl.x + tw, tl.y + th);
  50. // 绘制矩形
  51. Imgproc.rectangle(bg, tl, br, new Scalar(0, 0, 255), 2);
  52. // 保存图片
  53. Imgcodecs.imwrite(".\\result.png", bg);
  54. //返回图片缺口的位置,平移滑块我们只需要x轴坐标,
  55. // 这里缺口取中心点,或许会有些偏差,可自行调整
  56. return tl.x+tw/2;
  57. }
  58. //该函数用于读取从网络上下载的图片
  59. private Mat loadImageFromURL(String imageUrl) {
  60. try {
  61. URL url = new URL(imageUrl);
  62. byte[] imageData = readAllBytes(url.openStream());
  63. ByteBuffer buffer = ByteBuffer.wrap(imageData);
  64. return Imgcodecs.imdecode(new Mat(1, imageData.length, Core.CV_8UC1, buffer), Imgcodecs.IMREAD_COLOR);
  65. } catch (IOException e) {
  66. e.printStackTrace();
  67. return null;
  68. }
  69. }
  70. //该函数用于从网络上下载图片
  71. private byte[] readAllBytes(java.io.InputStream is) throws IOException {
  72. byte[] buffer = new byte[is.available()];
  73. int nRead;
  74. int total = 0;
  75. while ((nRead = is.read(buffer, total, buffer.length - total)) > 0) {
  76. total += nRead;
  77. if (total == buffer.length)
  78. buffer = Arrays.copyOf(buffer, buffer.length * 2);
  79. }
  80. return Arrays.copyOf(buffer, total);
  81. }

2.用Selenium平移滑块

        从上面OpenCV的代码,我们已经获取到了滑块需要的偏移量,接下来就可以用Selenium来模拟滑块平移操作了,代码如下:

  1. //加载驱动
  2. driverPath="path/path/chromedriver.exe";
  3. System.setProperty("webdriver.chrome.driver",driverPath);
  4. //打开浏览器
  5. WebDriver driver = new ChromeDriver();
  6. //跳转URL
  7. driver.get("http://......")
  8. //获取背景图和滑块图的元素
  9. WebElement bg_img= driver.findElement(By.xpath("//div[@id='xxxxx']"));
  10. WebElement sdr_img= driver.findElement(By.xpath("//div[@id='xxxxx']"));
  11. //获取背景图和滑块图SRC
  12. String bgPath = bg_img_src;
  13. String sdrParh = sdr_img_src;
  14. //获取滑块按钮元素,记得更改选择器
  15. WebElement slider= driver.findElement(By.xpath("//div[@id='xxxxx']"));
  16. //获取偏移量,用上前面咱们封装的函数
  17. double offset = sliderRobot.getSilderOffset(bgPath, sdrPath);
  18. if (slider != null){
  19. Actions actions = new Actions(driver);
  20. //因为后面要转成int,这样直接计算偏差会比较大,实际使用得用些算法来减小误差
  21. int totalSteps = 20;//分解为20步,可自己调,但不能不分解,否则验证无法通过
  22. int stepSize = (int) (offset / totalSteps); // 每一步的宽度
  23. // 点击滑块开始移动
  24. actions.click(slider).perform();
  25. for (int i = 1; i <= totalSteps; i++) {
  26. actions.moveByOffset(stepSize, 0).perform(); // 移动一步
  27. Thread.sleep(20); // 暂停20毫秒,控制移动速度
  28. }
  29. actions.release().perform();//释放
  30. }

 最终运行结果:

三、总结

至此,滑块验证码的自动化脚本就完成了,当然,只是一个简单的例子,真正使用的话得使用一些算法来减小偏移误差。哈哈哈哈,用Java来做web自动化,感觉自己就是个异类。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/1005620
推荐阅读
相关标签
  

闽ICP备14008679号