• java爬虫(九)htmlunit无界面浏览器程序库


    1.HtmlUnit简要介绍

    HtmlUnit是一款java的无界面浏览器程序库。它模拟HTML文档,并提供相应的API,允许您调用页面,填写表单,点击链接等操作,就像您在“正常”浏览器中做的一样。它有相当不错的JavaScript支持(还在不断改进),甚至能够处理相当复杂的AJAX库,模拟Chrome,Firefox或Internet Explorer取决于使用的配置。它通常用于测试目的或从网站检索信息。

    HtmlUnit不是一个通用的单元测试框架。它是一种模拟浏览器以用于测试目的的方法,并且旨在用于另一个测试框架(如JUnit或TestNG)中。有关简介,请参阅文档“HtmlUnit入门”。HtmlUnit用作不同的开源工具,如Canoo WebTest,JWebUnit,WebDriver,JSFUnit,WETATOR,Celerity,Spring MVC Test HtmlUnit作为底层的“浏览器”。

    HtmlUnit最初由Gargoyle Software的Mike Bowler编写,并根据Apache 2许可证发布。从那时起,它已经收到了许多来自其他开发商的贡献,今天也会得到他们的帮助。

    几年前在做一个购物网站的数据抓取工作中,偶然的机会邂逅了HtmlUnit了。记得当时怎么也捉取不到页面上的价格数据,而httpfox也追踪不到价格数据的URL,正当我一愁莫展的时个,HtmlUnit出现并帮我解决了问题。所以今天我要说声谢谢,也将HtmlUnit推荐给大家。

    2.htmlUnit中文文档

    3.1 获取页面的TITLE、XML代码、文本

    复制代码
    import com.gargoylesoftware.htmlunit.WebClient;
    import com.gargoylesoftware.htmlunit.html.HtmlPage;
    import com.gargoylesoftware.htmlunit.BrowserVersion;
    import com.gargoylesoftware.htmlunit.html.HtmlDivision;
    import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
    import com.gargoylesoftware.htmlunit.*;
    import com.gargoylesoftware.htmlunit.WebClientOptions;
    import com.gargoylesoftware.htmlunit.html.HtmlInput;
    import com.gargoylesoftware.htmlunit.html.HtmlBody;
    import java.util.List;
    
    public class helloHtmlUnit{
        public static void main(String[] args) throws Exception{
            String str;
            //创建一个webclient
            WebClient webClient = new WebClient();
            //htmlunit 对css和javascript的支持不好,所以请关闭之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            //获取页面
            HtmlPage page = webClient.getPage("http://www.baidu.com/");
            //获取页面的TITLE
            str = page.getTitleText();
            System.out.println(str);
            //获取页面的XML代码
            str = page.asXml();
            System.out.println(str);
            //获取页面的文本
            str = page.asText();
            System.out.println(str);
            //关闭webclient
            webClient.closeAllWindows();
        }
    }
    复制代码

    3.2 使用不同版本的浏览器打开

    复制代码
    import com.gargoylesoftware.htmlunit.WebClient;
    import com.gargoylesoftware.htmlunit.html.HtmlPage;
    import com.gargoylesoftware.htmlunit.BrowserVersion;
    import com.gargoylesoftware.htmlunit.html.HtmlDivision;
    import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
    import com.gargoylesoftware.htmlunit.*;
    import com.gargoylesoftware.htmlunit.WebClientOptions;
    import com.gargoylesoftware.htmlunit.html.HtmlInput;
    import com.gargoylesoftware.htmlunit.html.HtmlBody;
    import java.util.List;
    
    public class helloHtmlUnit{
        public static void main(String[] args) throws Exception{
            String str;
            //使用FireFox读取网页
            WebClient webClient = new WebClient(BrowserVersion.FIREFOX_24);
            //htmlunit 对css和javascript的支持不好,所以请关闭之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            HtmlPage page = webClient.getPage("http://www.baidu.com/");
            str = page.getTitleText();
            System.out.println(str);
            //关闭webclient
            webClient.closeAllWindows();
        }
    }
    复制代码

    3.3 找到页面中特定的元素

    public class helloHtmlUnit{
        public static void main(String[] args) throws Exception{
            //创建webclient
            WebClient webClient = new WebClient(BrowserVersion.CHROME);
            //htmlunit 对css和javascript的支持不好,所以请关闭之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            HtmlPage page = (HtmlPage)webClient.getPage("http://www.baidu.com/");
            //通过id获得"百度一下"按钮
            HtmlInput btn = (HtmlInput)page.getHtmlElementById("su");
            System.out.println(btn.getDefaultValue());
            //关闭webclient
            webClient.closeAllWindows();
        }
    }

     tips:有些元素中没有id和name或其他节点,可以通过找他的子节点和父节点之间规律的方法来获取该元素,具体方法参考:https://blog.csdn.net/qq_36237061/article/details/86533278

    其核心代码为:        

    final HtmlPage nextPage = ((DomElement)(htmlpage.getElementByName("key").getParentNode().getParentNode())).getLastElementChild().click();

    3.4 元素检索

    复制代码
    public class helloHtmlUnit{
        public static void main(String[] args) throws Exception{
            //创建webclient
            WebClient webClient = new WebClient(BrowserVersion.CHROME);
            //htmlunit 对css和javascript的支持不好,所以请关闭之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            HtmlPage page = (HtmlPage)webClient.getPage("http://www.baidu.com/");
            //查找所有div
            List<?> hbList = page.getByXPath("//div");
            HtmlDivision hb = (HtmlDivision)hbList.get(0);
            System.out.println(hb.toString());
            //查找并获取特定input
            List<?> inputList = page.getByXPath("//input[@id='su']");
            HtmlInput input = (HtmlInput)inputList.get(0);
            System.out.println(input.toString());
            //关闭webclient
            webClient.closeAllWindows();
        }
    }
    复制代码

    3.5 提交搜索

    复制代码
    public class helloHtmlUnit{
        public static void main(String[] args) throws Exception{
            //创建webclient
            WebClient webClient = new WebClient(BrowserVersion.CHROME);
            //htmlunit 对css和javascript的支持不好,所以请关闭之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            HtmlPage page = (HtmlPage)webClient.getPage("http://www.baidu.com/");
            //获取搜索输入框并提交搜索内容
            HtmlInput input = (HtmlInput)page.getHtmlElementById("kw");
            System.out.println(input.toString());
            input.setValueAttribute("ymd");
            System.out.println(input.toString());
            //获取搜索按钮并点击
            HtmlInput btn = (HtmlInput)page.getHtmlElementById("su");
            HtmlPage page2 = btn.click();
            //输出新页面的文本
            System.out.println(page2.asText());
        }
    }
    复制代码

    3.htmlUnit方法介绍

    一、环境引入
    因为我是在我自己的spring boot项目在引入的,所以我在pom文件中加入依赖就行了

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

    如果你只是爬取一个js不多的网站我建议换下面这个依赖

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

    两者的区别我后面会讲,当然如果你不是用的maven工程(没有pom),可以去官网下载源代码库
    二、使用
    HtmlUnit使用起来非常简单,在使用时可以去官网手册查看语法事实上手册仅仅是入门讲解,听我下面讲也够了;
    1、创建客户端和配置客户端

            
             final String url ="https:****";//大家这可以填自己爬虫的地址
            WebClient webClient = new WebClient(BrowserVersion.FIREFOX_68);//创建火狐浏览器 2.23版本是FIREFOX_45 new不写参数是默认浏览器
            webClient.getOptions().setCssEnabled(false);//(屏蔽)css 因为css并不影响我们抓取数据 反而影响网页渲染效率
            webClient.getOptions().setThrowExceptionOnScriptError(false);//(屏蔽)异常
            webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);//(屏蔽)日志
            webClient.getOptions().setJavaScriptEnabled(true);//加载js脚本
            webClient.getOptions().setTimeout(50000);//设置超时时间
            webClient.setAjaxController(new NicelyResynchronizingAjaxController());//设置ajax
            HtmlPage htmlPage = webClient.getPage(url);//将客户端获取的树形结构转化为HtmlPage
            Thread.sleep(10000);//主线程休眠10秒 让客户端有时间执行js代码 也可以写成webClient.waitForBackgroundJavaScript(1000)

    这里有个等待js执行,2.41.0很好兼容js多的情况,2.3却老是出问题,无法刷新网页,2.41.0打印的也很详细,执行过程也比较慢,可能慢工出细活
    这里差多就获取了远程地址页面,现在要做的就是解析dom节点 填写数据模拟点击等事件。如果你要将他打印输出 htmlPage.asText()输出htmlPage节点的文本 htmlPage.asXml()输出htmlPage节点的xml代码
    2、节点的获取
    在此环节建议预备一点前端的知识
    HtmlUnit给出两种节点获取方式
    XPath查询:

    比较详细的xpath讲解:https://testerhome.com/topics/20296

     final HtmlPage page = webClient.getPage("http://htmlunit.sourceforge.net");
    
            //get list of all divs
            final List<?> divs = htmlPage .getByXPath("//div");
    
            //get div which has a 'name' attribute of 'John'
            final HtmlDivision div = (HtmlDivision) htmlPage .getByXPath("//div[@name='John']").get(0);

    css选择器:(我更加钟爱)

      final DomNodeList<DomNode> divs = htmlPage .querySelectorAll("div");
            for (DomNode div : divs) {
                ....
            }
    
            //get div which has the id 'breadcrumbs'
            final DomNode div = htmlPage .querySelector("div#breadcrumbs");

    css给出集合查询querySelectorAll和单个查询querySelector,如果你没有基础我给出以下例子你就明白:
    htmlPage .querySelectorAll(“div”)返回htmlPage下面div标签集合
    htmlPage .querySelector(“div:nth-child(1)”)返回htmlPage下面div的第一个div
    htmlPage .querySelector(".submit")返回htmlPage下面第一个class=submit的标签
    htmlPage .querySelector("#submit")返回htmlPage下面第一个id=submit的标签
    htmlPage .querySelector(“div.submit”)返回htmlPage下面第一个class为submit的div标签
    htmlPage .querySelector(“div[id=‘submit’]”)返回htmlPage下面第一个id为submit的div标签
    以上的列举方法相信已经够用,不够的可以参阅css选择器
    以下列举常见的html标签与HtmlUnit类的对应关系

    div -> HtmlDivision
    div集合 -> DomNodeList<DomNode>
    fieldSet -> HtmlFieldSet 
    form -> HtmlForm
    button -> HtmlButton
    a -> HtmlAnchor
    <input type="xxx"> -> HtmlXxxInput
    (<input type="text"> -> HtmlTextInput)
    table -> HtmlTable
    tr -> HtmlTableRow
    td -> TableDataCell

    有 setAttribute()方法节点的属性样式,setNodeValue()设置节点value值。是不是英语一下子就提高了?几乎所有的标签可以找到与之对应的的类,下面看我的实战:这是一个在线填写温度的excel文档 如果访问改地址,他会提示登陆网页上有登录按钮,如果登录过网页上是没有登录按钮,我们现在模拟打开自动登录框:

    //这段代码是为了让网页的的某个按钮加载出来之后再执行下面的代码
            while (htmlPage.querySelector("#header-login-btn")==null) {
                synchronized (htmlPage) {
                    htmlPage.wait(1000);
                }
            }
            HtmlButton login = htmlPage.querySelector("#header-login-btn");//获取到登陆按钮
            if (login!=null){//如果网页上没这个按钮你还要去获取他只会得到一个空对象,所以我们用空的方式可以判断网页上是否有这个按钮
                System.out.println("-----未登录测试-----");
                htmlPage=login.click();//模拟按钮点击后要将网页返回回来方便动态更新数据
                System.out.println(htmlPage.asText());
                HtmlAnchor switcher_plogin = htmlPage.querySelector("a[id='switcher_plogin']");
                if (switcher_plogin!=null) {//帐号密码登录
                    System.out.println("-----点击了帐号密码登陆-----");
                    htmlPage = switcher_plogin.click();
                    System.out.println(htmlPage.asText());
                }
            }
            System.out.println(htmlPage.asText());
            webClient.close();

    爬虫最重要的步骤是我们对网页先进行调试,有哪些按钮,要点击哪个,要给哪些设值。毕竟我们要用代码安排代码。
    **延申:**如果你要对网页的数据获取或者文件进行下载,光靠HtmlUnit解析是不够的,推荐Jsoup库,是可以配合HtmlUnit使用的,比较好用,这里就不列举。

    三、实现一个小的demo

    注意:htmlunit引用的jar包不全是会奇怪的报错

     使用maven方法比较方便

    参考:https://blog.csdn.net/weixin_44787678/article/details/106994485

  • 相关阅读:
    17、Semantic-UI之分页插件
    16、Semantic-UI之模态窗口
    15、Semantic-UI之导航
    14、Semantic-UI之菜单样式
    13、Semantic-UI之表格与表单
    12、Semantic-UI之输入框
    11、Semantic-UI之分割线
    10、Semantic-UI之图片的使用
    9、Semantic-UI之标题
    8、Semantic-UI之其他按钮样式
  • 原文地址:https://www.cnblogs.com/StarZhai/p/14221635.html
Copyright © 2020-2023  润新知