java爬虫遇到网页验证码怎么办?(使用selenium模拟浏览器并用python脚本解析验证码图片)

这篇具有很好参考价值的文章主要介绍了java爬虫遇到网页验证码怎么办?(使用selenium模拟浏览器并用python脚本解析验证码图片)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        笔者这几天在爬取数据的时候遇到了一个很闹心的问题,就是在我爬取数据的时候遇到了验证码,而这个验证码又是动态生成的,尝试了很多方法都没能绕开这个验证码问题。

        我的解决方案是:使用selenium模拟浏览器行为,获取到动态生成的验证码后用python脚本解析验证码图片,返回验证码的值,再用selenium输入该值,进行下一步的爬取工作。

目录

使用selenium模拟浏览器行为

使用selenium截取到验证码图片

将验证码图片保存到本地

使用python脚本解析验证码图片

如果使用ddddocr库报错

使用java调用python脚本

获取解析得到的验证码并填入

项目源代码

总结


使用selenium模拟浏览器行为

1、首先需要导入selenium依赖:

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.11.0</version> <!-- 使用最新的稳定版本 -->
        </dependency>

        我这里是使用的Chrome浏览器,需要下载Chrome驱动,使用其他浏览器也是同理,下载好浏览器驱动后找到该驱动的路径(我的路径是“C:\\ChromeDriver\\chromedriver.exe”):

    System.setProperty("webdriver.chrome.driver", "C:\\ChromeDriver\\chromedriver.exe");
    ChromeOptions options = new ChromeOptions();
    //通过配置参数禁止data;的出现,不会弹出浏览器,默认是后台静默运行
    //options.addArguments("--headless","--disable-gpu");
    //最大化浏览器窗口,否则location定位可能不准
    options.addArguments("--disable-blink-features=AutomationControlled");
    options.addArguments("--start-maximized");
    // 创建 ChromeDriver 对象
    WebDriver driver = new ChromeDriver();
    driver.get(url);

这里的location定位指的是定位后续会出现的验证码图片 

2、这里执行get(url)之后,如果你没有额外设置一些参数,系统会自动唤起你的Chrome浏览器并跳转到该url,此时重点来了,需要输入搜索参数:

public static void doSearch(WebDriver driver, String filterName, String eudName) {
        //两个搜索参数
        driver.findElement(By.id("filterName")).sendKeys(filterName);
        driver.findElement(By.id("eudName")).sendKeys(eudName);
        //触发"搜索"按钮的点击事件
        driver.findElement(By.xpath("/html/body/div[2]/div/div[1]/div[1]/form/div/div[5]/input")).click();
    }

         这里需要打开网页的开发者工具,找到对应的搜索框的id或xpath等,只要能定位到这个元素即可,然后使用sendKeys()方法,就可以往搜索框内输入参数,然后找到“搜索”这个按钮,定位一下,使用click()方法触动点击事件,完成selenium模拟浏览器实现带参数的搜索。

这里多说一句,可以在网页的开发者工具里找到需要定位的元素之后右键copy,可以选择直接copy下来该元素的xpath

使用selenium截取到验证码图片

        由于我爬取的网站的验证码资源是带时间戳的,暂时没想到什么比较好的方法能够直接从网站上定位到该资源的位置从而下载下来,所以我这里使用了selenium的截图方法获取到该验证码。

public static BufferedImage getImage(WebDriver driver){
        //根据验证码图片的id属性定位到该元素
        WebElement img = driver.findElement(By.id("dynamic_img_code"));
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Point location = img.getLocation();
        Dimension size = img.getSize();
        //这里统一乘1.25是因为电脑的缩放比是125%,乘1.25能让location的定位精确
        int left = (int) (location.getX() * 1.25);
        int top = (int) (location.getY() * 1.25);
        int right = (int) (left + size.getWidth() * 1.25);
        int bottom = (int) (top + size.getHeight() * 1.25);
        File screenshot = ((ChromeDriver) driver).getScreenshotAs(org.openqa.selenium.OutputType.FILE);
        BufferedImage fullImage = null;
        try {
            fullImage = ImageIO.read(screenshot);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return fullImage.getSubimage(left, top, right - left, bottom - top); // 得到的就是验证码
    }

        这段代码的思路是先获取全屏截图,然后根据验证码图片的location定位到,获取全屏截图的子截图,即验证码图片。

将验证码图片保存到本地

上一步截取到验证码图片之后将其保存到本地,方便后续使用python脚本对其进行解析:

BufferedImage image = getImage(driver);
try {
    ImageIO.write(image, "png", new File(outputDir,"captcha.png"));
} catch (IOException e) {
    throw new RuntimeException(e);
}
这里的outputDir也是File类型的,代指captcha图片存放的文件夹

使用python脚本解析验证码图片

这里我使用了ddddocr库

pip install ddddocr

        这是一个github上的免费开源项目,我爬取的网站的验证码是纯数字的,这个库的识别正确率很高,同时这个库还支持其他类型的验证码的识别,我没有研究,有需求的可以进作者的github了解

解析验证码的python脚本:

import ddddocr
import sys

# def get_captcha(path):
#     ocr = ddddocr.DdddOcr()
#     with open(path, "rb") as f:
#         img_bytes = f.read()
#     res = ocr.classification(img_bytes)
#     return res


if __name__ == "__main__":
        path = sys.argv[1]
        ocr = ddddocr.DdddOcr()
        with open(path, "rb") as f:
            img_bytes = f.read()
        res = ocr.classification(img_bytes)
        print(res)

如果使用ddddocr库报错

报错提示为:

AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘

可以参照这个解决: https://blog.csdn.net/light2081/article/details/131517132

使用java调用python脚本

        如果你的python是3.0以上,就不要使用jython了,jython已经停止更新不支持3.0以上的python了,我这里是采用的进程调用的方法,需要传入的两个参数分别为python脚本路径和需要解析的验证码图片路径:

public static String pythonGetCaptcha(String pythonScriptPath,String imagePath){
        String captcha = null;
        try {
            // 1. 构建命令行执行的命令
            String[] command = {"python", pythonScriptPath, imagePath};
            // 2. 执行命令
            Process process = Runtime.getRuntime().exec(command);

            // 3. 获取命令输出
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            StringBuilder captchaBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                captchaBuilder.append(line);
            }
            captcha = captchaBuilder.toString();
            inputStream.close();
            reader.close();

            // 4. 等待命令执行完毕并获取返回值
        } catch (IOException e) {
            e.printStackTrace();
        }
        return captcha;
    }

获取解析得到的验证码并填入

        到这一步基本就结束了,只需要把解析得到的验证码填入框内点击提交即可,原理跟之前的doSearch()一样:

        String captcha = CaptchaPictureUtil.pythonGetCaptcha(pythonScriptPath, outputDir.getPath()+"/captcha.png");
        driver.findElement(By.id("dynamic_imgcode")).sendKeys(captcha);
        driver.findElement(By.id("dynamic_submit")).click();
        try {
            Thread.sleep(5000);
            html = driver.getPageSource();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        driver.quit(); // 一定要退出!不退出会有残留进程!

项目源代码

package com.spidertest.Utils;

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;

public abstract class CaptchaPictureUtil {

    /**
     * @since 2023/8/6
     * @author HWJ
     * @DESC 针对所爬取的网页特地做的爬虫工具类,因为爬虫的过程中会出现验证码,而验证码是输入了搜索参数之后才会出现
     * @param url
     * @param filterName
     * @param eudName
     * @param outputDir
     */
    public static String getCaptchaPicture(String url,String filterName,String eudName,File outputDir,String pythonScriptPath){
        String html = null;
        System.setProperty("webdriver.chrome.driver", "C:\\ChromeDriver\\chromedriver.exe");
        ChromeOptions options = new ChromeOptions();

        //通过配置参数禁止data;的出现,不会弹出浏览器,默认是后台静默运行
        //options.addArguments("--headless","--disable-gpu");
        //最大化浏览器窗口,否则location定位可能不准
        options.addArguments("--disable-blink-features=AutomationControlled");
        options.addArguments("--start-maximized");


        // 创建 ChromeDriver 对象,同时传入 ChromeOptions 对象
        WebDriver driver = new ChromeDriver(options);

        driver.get(url);
        doSearch(driver, filterName, eudName);
        BufferedImage image = getImage(driver);
        try {
            ImageIO.write(image, "png", new File(outputDir,"captcha.png"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String captcha = CaptchaPictureUtil.pythonGetCaptcha(pythonScriptPath, outputDir.getPath()+"/captcha.png");
        driver.findElement(By.id("dynamic_imgcode")).sendKeys(captcha);
        driver.findElement(By.id("dynamic_submit")).click();
        try {
            Thread.sleep(5000);
            html = driver.getPageSource();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        driver.quit(); // 一定要退出!不退出会有残留进程!
        return html;
    }

    /**
     * @since 2023/8/6
     * @author HWJ
     * @DESC 针对所爬取的网站让selenium做的模拟浏览器发起的请求,目的是为了触发网页弹出验证码
     * @param driver
     * @param filterName
     * @param eudName
     */
    public static void doSearch(WebDriver driver, String filterName, String eudName) {
        //两个搜索参数
        driver.findElement(By.id("filterName")).sendKeys(filterName);
        driver.findElement(By.id("eudName")).sendKeys(eudName);
        //触发"搜索"按钮的点击事件
        driver.findElement(By.xpath("/html/body/div[2]/div/div[1]/div[1]/form/div/div[5]/input")).click();
    }

    public static BufferedImage getImage(WebDriver driver){
        WebElement img = driver.findElement(By.id("dynamic_img_code"));
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Point location = img.getLocation();
        System.out.println(location);
        Dimension size = img.getSize();
        System.out.println(size);
        //这里统一乘1.25是因为电脑的缩放比是125%,乘1.25能让location的定位精确
        int left = (int) (location.getX() * 1.25);
        int top = (int) (location.getY() * 1.25);
        int right = (int) (left + size.getWidth() * 1.25);
        int bottom = (int) (top + size.getHeight() * 1.25);
        File screenshot = ((ChromeDriver) driver).getScreenshotAs(org.openqa.selenium.OutputType.FILE);
        BufferedImage fullImage = null;
        try {
            fullImage = ImageIO.read(screenshot);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return fullImage.getSubimage(left, top, right - left, bottom - top); // 得到的就是验证码
    }

    /**
     * @since 2023/8/6
     * @DESC 使用py脚本解析验证码图片获取验证码
     * @param pythonScriptPath
     * @param imagePath
     * @return captcha
     */
    public static String pythonGetCaptcha(String pythonScriptPath,String imagePath){
        String captcha = null;
        try {
            // 1. 构建命令行执行的命令
            String[] command = {"python", pythonScriptPath, imagePath};
            // 2. 执行命令
            Process process = Runtime.getRuntime().exec(command);

            // 3. 获取命令输出
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            StringBuilder captchaBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                captchaBuilder.append(line);
            }
            captcha = captchaBuilder.toString();
            inputStream.close();
            reader.close();

            // 4. 等待命令执行完毕并获取返回值
        } catch (IOException e) {
            e.printStackTrace();
        }
        return captcha;
    }
}

总结

        本人是爬虫小白,在做这个爬取工具之前也只有两天学习爬虫的经历,本来是打算用HttpClient和JSoup做爬虫的,但是遇到了烦人的验证码,只能用这种方法解决。这个代码依旧有很多不足的地方需要改进,同时由于是针对我需要爬取的网站所编写的爬虫,耦合度还是很高,所以我尽量解释了我的想法,阅读者可以根据自己的需要找到有启发的地方就再好不过了。

        这也是我的第一篇博客,发出来是为了记录一下这两天的工作,代码和博客内容还有很多不足,欢迎大家批评指正。

ps:上述代码的部分启发如下()
https://www.cnblogs.com/xuehaiwuya0000/p/11509435.html
https://blog.csdn.net/light2081/article/details/131517132
https://github.com/sml2h3/ddddocr
https://www.selenium.dev/documentation/文章来源地址https://www.toymoban.com/news/detail-696426.html

到了这里,关于java爬虫遇到网页验证码怎么办?(使用selenium模拟浏览器并用python脚本解析验证码图片)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 网页打不开怎么办?使用路由器后有些网站打不开的解决方法

    有很多网友会遇到这样的问题 路由器连接网络有些网站打不开, 有些常用的到能打开有些网站却打不开了我可以确定是有网络的, 其实问题不是网络也不是电脑的硬件,是没设置好NDS, 下面来告诉你们怎么设置吧  ,高手请无视内容! 1、首先回到电脑桌面 桌面上找到-网

    2024年02月07日
    浏览(36)
  • 遇到Spring事务失效,你该怎么办?

    遇到Spring事务失效,你该怎么办?

    Spring 事务场景失效是一个常见的问题。今天来分析这个问题。 失效原因 事务方法被final、static修饰:这是因为Spring事务的实现依赖于AOP技术,而final、static方法无法被代理,因此在这些方法中调用事务方法,事务无法生效。 方法访问权限不是public:Spring事务的实现也

    2023年04月15日
    浏览(6)
  • 遇到台式电脑关不了机该怎么办

    遇到台式电脑关不了机该怎么办

    ​ 台式电脑关不了机是怎么回事?每天都有在用着电脑的朋友可能都会有遇到这样的一个问题,其实这个问题大多数时候是没有正确关机导致的,下面我们就来看看遇到这种情况是什么原因又该如何解决吧。 还有其它电脑重装系统方法可参考装机吧网        工具/原料 系统

    2024年02月08日
    浏览(29)
  • 遇到时间控件怎么办?不要慌,教你轻松拿下

    遇到时间控件怎么办?不要慌,教你轻松拿下

    此文章来源于项目官方公众号:“AirtestProject” 版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途 很多同学在测试场景中总会遇到各种各样的小控件需要进行测试的,包括在Android端,web端等等都有,那么今天我们来看看当我们在遇到时间控件的时候

    2024年03月28日
    浏览(2)
  • 网页内容包含敏感字该怎么办?

    嗨,大家好!今天咱们来聊聊一个非常重要的话题——网页内容包含敏感字的危害。这可不是小事,影响可大了! 首先,得搞明白什么是敏感字。这指的是那些可能引起不适或冒犯的词汇,可能涉及到政治、宗教、性别等方面的敏感议题。会给企业带来用户流失、法律责任等

    2024年01月19日
    浏览(6)
  • 遇到移动号码手机停机怎么办?如何自助解决上网问题?

    遇到移动号码手机停机怎么办?如何自助解决上网问题?

    今天是 2022年4月1日,是的,愚人节。 一大早去赶地铁上班(深圳高峰期挤地铁特别是 1 号线简直被挤爆)的路上,发现上不了网了!原来是话费停机欠费了,忘了提前充话费。想回家连个 WiFi 但是出门很远了,还是算了。 后来我想:要不要随便去一家门店蹭个 WiFi?毕竟早

    2024年02月16日
    浏览(30)
  • 阿里云haas100遇到hidapi.h错误怎么办?

    阿里云haas100遇到hidapi.h错误怎么办?

    摘要:使用vscode拉取ucloud_ai_demo例程后,会出现缺少hidapi.h的错误。原因不明,但是进行简单的修改,就可以正常编译通过了,详见正文。 例程的位置是 ucloud_ai_demo: ucloud ai demo code https://gitee.com/alios-things/ucloud_ai_demo ucloud_ai_demo是基于云端AI能力实现的AI识别案例,主要有三个部

    2024年02月01日
    浏览(6)
  • Python爬虫,请求参数加密怎么办?

    Python爬虫,请求参数加密怎么办?

    目录 背景介绍: 目标网址: 页面分析: 逆向解析加密参数思路 代码实现: code_js.js JS方式实现 python代码实现 总结:                                    我是政胤 期待你的关注 背景介绍: 大家好 我是 政胤. 我们在请求接口的时候,发现请求参数数加密的,该如何处理

    2024年02月11日
    浏览(11)
  • 单片机遇到“auto segment too large“怎么办

    遇到这个情况有可能是某一个数组太大,然后单片机内部数据存储区不够用, 所以可以把这个数据存在单片机外例如      int led[8][8]={         {1,1,1,1,1,1,1,1},//1         {1,0,0,0,0,0,0,1},//2         {1,0,0,0,0,0,0,1},//3         {1,0,0,0,0,0,0,1},//4         {1,0,0,0,0,0,0,1},//5  

    2024年02月14日
    浏览(4)
  • Unity Hub下载中文一直验证中怎么办

    Unity Hub是Unity官方提供的一款管理Unity引擎和项目的工具。然而,有时在下载中文版的Unity Hub时可能会遇到“验证中”的情况,这可能会导致下载进程无法继续。本文将介绍一些可能的解决方法,帮助您处理Unity Hub下载中文版本出现“验证中”问题。 清除缓存和重试: 第一种

    2024年02月14日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包