java通过HtmlUnit工具和J4L实现模拟带验证码登录

1.HtmlUnit

1.1介绍

HtmlUnit是一个用java编写的无界面浏览器,建模html文档,通过API调用页面,填充表单,点击链接等等。如同正常浏览器一样操作。典型应用于测试以及从网页抓取信息。

官方简介翻译
HtmlUnit是一个无界面浏览器Java程序。它为HTML文档建模,提供了调用页面、填写表单、单击链接等操作的API。就跟你在浏览器里做的操作一样。 HtmlUnit不错的JavaScript支持(不断改进),甚至可以使用相当复杂的AJAX库,根据配置的不同模拟Chrome、Firefox或Internet Explorer等浏览器。 HtmlUnit通常用于测试或从web站点检索信息。

1.2使用场景

  • httpClient的局限性

对于使用java实现的网页爬虫程序,我们一般可以使用apache的HttpClient组件进行HTML页面信息的获取,HttpClient实现的http请求返回的响应一般是纯文本的document页面,即最原始的html页面。

对于一个静态的html页面来说,使用httpClient足够将我们所需要的信息爬取出来了。但是对于现在越来越多的动态网页来说,更多的数据是通过异步JS代码获取并渲染到的,最开始的html页面是不包含这部分数据的。

 

上图我们所见到的网页,在最初的document加载完成之后,并不会看到红框中的数据列表。浏览器通过执行异步JS请求,将获取到的动态数据,渲染到最初的document页面中,才最终变成了我们看到的网页。而对于这部分需要执行JS代码获取的数据,httpClient就显得无能为力了。虽然我们可以通过研究拿到JS执行的请求路径再用java代码获取我们需要的这部分数据,且不说我们能不能够从JS脚本中分析到这个请求路径和请求参数,光是分析这部分源码的代价就已经很高了。

  • HtmlUnit来解决

通过上面的介绍,我们了解了现在很大一部分动态网页,展现的数据都是通过异步JS请求获取,然后再通过JS对页面进行渲染得到的。那我们是不是可以进行这么一个假设,假设我们的爬虫程序模拟了一个浏览器,在获取html页面之后,像浏览器一样执行异步JS代码,等到JS将html页面渲染完成之后,就可以愉快的获取页面上的节点信息了。那么有没有这样的java程序呢?

答案是有的。

HtmlUnit就是这么一个程序库,用来做出了界面展示意外所有的异步工作。由于没有了展示这一块耗时的工作,HtmlUnit加载完成一个完整的网页要比实际的浏览器块多了。并且根据不同配置,HtmlUnit可以模拟市面上常用的浏览器如Chrome、Firefox、IE浏览器等。

通过HtmlUnit库,加载一个完整的Html页面(图片视频除外),然后就可以将其转换成我们常用的字串格式,用其他工具如Jsoup来获取其中的元素了。当然也可以直接在HtmlUnit提供的对象中获取网页元素,甚至是操作如按钮、表单等控件。除了不能像可见浏览器一样用鼠标键盘浏览网页之外,我们可以用HtmlUnit来模拟操作其他的一切操作,像登录网站,撰写博客等等都是可以完成的。当然网页内容爬取是最简单的一个应用了。

 

2.J4L

2.1官方简介

The J4L OCR tools is set of components that can be used to include OCR capabilities in Java applications. That means you can receive faxes, PDF files or scan documents and extract business information from the images. The main 3 components are:

  • a Java wrapper for the Tesseract OCR engine. The OCR engine Tesseract itself is delivered under the Apache 2.0 license and we support a version compiled for windows only.
  • a PDF to text converter. 
  • a text document parser.

关于j4l工具详细使用步骤可以参考本篇文章 : Java使用J4L识别验证码

本篇文章主要介绍使用HtmlUnit模拟登陆

3.实际案例

3.1.场景

一个登陆页面,包含一个表单,需要填写用户名、登陆密码、验证码实现用户登录。

3.2操作步骤

使用htmlunit模拟登录,就是一个获取登录页面->找到输入框->填入用户名密码->填写验证码->模拟点击登录的过程。 

4.实现代码

4.1maven引入HtmlUnit依赖

<dependency>
  <groupId>net.sourceforge.htmlunit</groupId>
  <artifactId>htmlunit</artifactId>
  <version>2.18</version>
</dependency>

4.2主方法

Login.class

    public static void main(String[] args) throws Exception {
        login();
    }

    public static void login() throws Exception {
        WebClient webClient = new WebClient(BrowserVersion.CHROME);//设置浏览器内核
        webClient.getOptions().setJavaScriptEnabled(true);// js是否可用
        webClient.getOptions().setCssEnabled(true);//css 一般设置false因为影响运行速度
        webClient.getOptions().setThrowExceptionOnScriptError(false);//设置js抛出异常:false
        webClient.getOptions().setActiveXNative(false);

        //ajax
        webClient.setAjaxController(new NicelyResynchronizingAjaxController());
        webClient.getOptions().setUseInsecureSSL(false);
        //允许重定向
        webClient.getOptions().setRedirectEnabled(true);
        //连接超时
        webClient.getOptions().setTimeout(5000);
        //js执行超时
        webClient.setJavaScriptTimeout(10000 * 3);
        //允许cookie
        webClient.getCookieManager().setCookiesEnabled(true);
        String url = "http://hrportalneu.cs/HRPortalNeu/check.aspx";
        HtmlPage page = webClient.getPage(url);
        webClient.waitForBackgroundJavaScript(5000);
        HtmlPage newPage;
        newPage = readyPage(page, webClient, "btnCheckIn1");
        System.out.println("url----------------" + newPage.getUrl());
        System.out.println("Login执行后页面:" + newPage.asXml());
        IOUtils.write(newPage.asXml().getBytes(), new FileWriter(new File("D:/checkIn.txt")));
        if (!newPage.asXml().contains("登录成功")) {
            login();
        }
        webClient.close();
    }


    public static HtmlPage readyPage(HtmlPage page, WebClient webClient, String type) throws Exception {
        //封装页面元素
        HtmlForm form = page.getHtmlElementById("form1");
        HtmlTextInput tbxUsername = form.getInputByName("tbxUsername");
        tbxUsername.setValueAttribute("用户名*******");
        HtmlPasswordInput tbxPassword = form.getInputByName("tbxPassword");
        tbxPassword.setValueAttribute("密码*******");

        //获取验证码图片
        HtmlImage verify_img = (HtmlImage) page.getElementById("AuthCode1$codeText$img");

        File file = new File("D:\\" + "check" + ".png");
        //保存验证码图片
        verify_img.saveAs(file);
        System.out.println("验证码图片已保存!");

        //保存路径
        String filePath = file.getPath();
        //裁剪验证码  这里使用原始的图片文件识别成功率不高 可以先进行裁剪
        ImageUtil.cutImage(filePath, 1, 1, 96, 34);
        //自动识别验证码
        OCRFacade facade = new OCRFacade();
        //识别验证码
        String code = facade.recognizeFile(filePath, "eng");
        //处理字符串
        code = code.replaceAll(" ", "");
        code = code.replaceAll("\\n", "");
        System.out.println("验证码:" + code);

        //定位验证码输入框
        HtmlTextInput verify_code = form.getInputByName("AuthCode1$codeText");
        //填入自动识别出来的验证码
        verify_code.setValueAttribute(code);

        System.out.println("原始页面:" + page.asXml());

        HtmlPage newPage = null;
        //操作按钮
        if (type.equals("btnCheckIn1")) {
            DomElement btnCheckIn1 = page.getElementById("btnCheckIn1");
            System.out.println("doCheckIn");
            newPage = btnCheckIn1.click();
        }

        //等待js加载
        webClient.waitForBackgroundJavaScript(5000);
        return newPage;
    }

  

ImageUtil.class

public class ImageUtil {

    public static void cutImage(String filePath, int x, int y, int w, int h)
            throws Exception {
        // 首先通过ImageIo中的方法,创建一个Image + InputStream到内存
        ImageInputStream iis = ImageIO
                .createImageInputStream(new FileInputStream(filePath));
        // 再按照指定格式构造一个Reader(Reader不能new的)
        Iterator it = ImageIO.getImageReadersByFormatName("gif");
        ImageReader imagereader = (ImageReader) it.next();
        // 再通过ImageReader绑定 InputStream
        imagereader.setInput(iis);

        // 设置感兴趣的源区域。
        ImageReadParam par = imagereader.getDefaultReadParam();
        par.setSourceRegion(new Rectangle(x, y, w, h));
        // 从 reader得到BufferImage
        BufferedImage bi = imagereader.read(0, par);

        // 将BuffeerImage写出通过ImageIO
        ImageIO.write(bi, "png", new File(filePath));

    }
}

在控制台查看Login执行后的页面,查看是否登录成功。

 

以上就是本篇文章的全部内容,如有不正确的地方,欢迎讨论。

转载于:https://www.cnblogs.com/sueyyyy/p/11394324.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值