转载出处:http://blog.csdn.net/wxl901018/article/details/44133873

1. 简介

htmlunit是一款开源的Java页面分析工具,读取页面后,可以有效的使用htmlunit 分析页面上的内容。项目可以模拟浏览器运行,被誉为Java浏览器的开源实现。这个没有界面的浏览器,运行速度也是非常迅速的。

2.起步

1.介绍

pom.xml加入的依赖

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

com.gargoylesoftware.htmlunit.WebClient类是起步的关键,它是一个仿真浏览器,其将会被用来执行所有测试。
大多数的测试单元将在一个想Junit的框架下进行,所有假设所有的例子我们都这样进行。
在第一个例子中,我们创建一个web客户端,使用该客户端来下载HtmlUnit网站的首页。然后我们确保这个页面有正确的标题。注意,根据返回数据的内容类型,getPage() 可以返回不同类型的页面。在这个例子中,我们期待 text/html 内容类型的页面,以致我们可以将结果存在com.gargoylesoftware.htmlunit.html.HtmlPage。

       @Testpublicvoid homePage() throws Exception {//创建web客户端final WebClient webClient = new WebClient();//设置链接地址final HtmlPage page =webClient.getPage("http://htmlunit.sourceforge.net");//断言页面的标题,不成立时报错Assert.assertEquals("HtmlUnit -Welcome to HtmlUnit", page.getTitleText());//获取页面的Xml代码final String pageAsXml = page.asXml();//断言页面包含Assert.assertTrue(pageAsXml.contains("<bodyclass=\"composite\">"));//获取页面的文本内页final String pageAsText = page.asText();//断言页面内容包含Assert.assertTrue(pageAsText.contains("Supportfor the HTTP and HTTPS protocols"));//关闭所有窗口webClient.closeAllWindows();}

2.模仿特定浏览器

有时你想模仿一个特殊的浏览器,这可以通过WebClient构造函数的com.gargoylesoftware.htmlunit.BrowserVersion 参数实现,其中已经提供一些常见浏览器的常量,但是,你可以通过BrowserVersion 的实例说明创建你自己拥有的特殊版本。

       @Testpublicvoid homePage_Firefox() throws Exception {//创建并指定web客户端的类型,在此为FireFoxfinal WebClient webClient = new WebClient(BrowserVersion.FIREFOX_17);final HtmlPage page =webClient.getPage("http://htmlunit.sourceforge.net");Assert.assertEquals("HtmlUnit -Welcome to HtmlUnit", page.getTitleText());webClient.closeAllWindows();}

指定这个BrowserVersion 会改变用户代理发送到服务器的报头,也会改变一些JavaScript 的行为。

3.查找特殊元素

一旦有一个HtmlPage的引用,你就可以通过其中的get方法或者使用 Xpath 搜索特殊的HtmlElement.下面是一个通过ID查找div 和通过name 获取 A 标签的超链接的例子。

       @Testpublicvoid getElements() throws Exception {final WebClient webClient = new WebClient();//将http://some_url改为一个网址,如http://www.baidu.com/final HtmlPage page =webClient.getPage("http://some_url");//将some_div_id改为特定ID。需根据page代码而定,如百度可以将id设为wrapperfinal HtmlDivision div =page.getHtmlElementById("some_div_id");//将anchor_name改为指定的name,也许因页面代码而定,如百度可以将其设为tj_settingiconfinal HtmlAnchor anchor =page.getAnchorByName("anchor_name");webClient.closeAllWindows();}

在更复杂的搜索中,Xpath 是建议方法,其可以在W3Schools教程中发现学习。

       @Testpublicvoid xpath() throws Exception {final WebClient webClient = new WebClient();final HtmlPage page =webClient.getPage("http://htmlunit.sourceforge.net");//获取所有div的列表final List<?> divs =page.getByXPath("//div");       //获取有name属性为John的divfinal HtmlDivision div = (HtmlDivision)page.getByXPath("//div[@name='John']").get(0);   webClient.closeAllWindows();}

4.使用代理服务器

WebClient 的最后一个构造函数允许你指定代理服务器信息,在这种情况下,你的连接需要经过代理服务器。

       @Testpublicvoid homePage_proxy() throws Exception {//需指定myProxyPortfinal WebClient webClient = new WebClient(BrowserVersion.FIREFOX_10, "myproxyserver",myProxyPort);//设置代理的用户名和密码final DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider)webClient.getCredentialsProvider();credentialsProvider.addCredentials("username","password");             final HtmlPage page =webClient.getPage("http://htmlunit.sourceforge.net");Assert.assertEquals("HtmlUnit -Welcome to HtmlUnit", page.getTitleText());          webClient.closeAllWindows();}

指定这个BrowserVersion 会改变用户代理发送到服务器的报头,也会改变一些JavaScript 的行为。

5.提交表单

我们经常想改变一个表单的值并提交会服务器。下面的例子将示例你如何去做。

       @Testpublicvoid submittingForm() throws Exception {final WebClient webClient = new WebClient();        // 获取第一页,修改http://some_url为指定连接final HtmlPage page1 =webClient.getPage("http://some_url");             // 获取我们需要处理的表单,并在里面找到提交按钮和我们需要修改的域//指定myform为特定name,如百度首页可为ffinal HtmlForm form =page1.getFormByName("myform");//指定submitbutton为指定Id,如百度首页可为sufinal HtmlSubmitInput button =form.getInputById("submitbutton");//获取修改域,如百度可将userid改为wdfinal HtmlTextInput textField =form.getInputByName("userid");// 修改域的值textField.setValueAttribute("root");// 通过点击按钮提交表单并获取返回页面final HtmlPage page2 = button.click();webClient.closeAllWindows();
}

3. 使用 Keyboards

对于给定的WebClient ,在任何时候焦点定在一个元素上,没有必要把焦点定在WebClient 里面的任意元素上。这里有几种方法,可以将焦点从一个元素移动到另一个元素上。最简单的方法就是调用HtmlPage.setFocusedElement(HtmlElement),这种方法将会把焦点从任何获得焦点的元素移动到指定的新组件上,沿途将会触发已经定义的所有的 onfocus和 onblur 方法。
调用HtmlPage.getFocusedElement() 可以确定当前获得焦点的元素。通过tab 键模拟键盘导航,你可以调用HtmlPage.tabToNextElement() 和HtmlPage.tabToPreviousElement() ,通过定义的tab顺序循环向前或向后。这个tab 顺序可以通过在很多元素的tabindex属性定义,想HTML 规范那样。
HtmlPage.getTabbableElements()方法返回所有可标签化的元素已定义的顺序的列表,可以通过此方法查询已定义的tab 顺序。
快捷键可以调用键盘的助记符,其可以方法HtmlPage.pressAccessKey(char)模拟。为了使用特殊的键,你可以使用htmlElement.type(int)方法,参数为KeyboardEvent.DOM_VK_PAGE_DOWN。
最后需要断言每一个可标签化的元素都定义了tabindex属性,可以通过WebAssert.assertAllTabIndexAttributesSet()实现。

4.使用Table

简单表格例子

第一组简单的例子将会是用下面的html文件

<html><head><title>Tablesample</title></head><body><table id="table1"><tr><th>Number</th><th>Description</th></tr><tr><td>5</td><td>Bicycle</td></tr></table></body></html>

下面这个例子将展示所有的行和单元格是如何迭代的

finalHtmlTable table = page.getHtmlElementById("table1");for(final HtmlTableRow row : table.getRows()) {System.out.println("Found row");for (final HtmlTableCell cell :row.getCells()) {System.out.println("   Found cell: " + cell.asText());}}

下面这个例子将会展示如何访问指定的行和列

       final WebClient webClient = new WebClient();finalHtmlPage page = webClient.getPage("http://foo.com");//通过id获取表格finalHtmlTable table = page.getHtmlElementById("table1");//打印第一行第二列System.out.println("Cell(1,2)=" + table.getCellAt(1,2));

复杂表格例子

这一组例子将会使用更复杂的表格,表格包括表头、脚注和主体以及一个标题。

<html><head><title>Tablesample</title></head><body><table id="table1"><caption>My complextable</caption><thead><tr><th>Number</th><th>Description</th></tr></thead><tfoot><tr><td>7</td><td></td></tr></tfoot><tbody><tr><td>5</td><td>Bicycle</td></tr></tbody><tbody><tr><td>2</td><td>Tricycle</td></tr></tbody></table></body></html>

HtmlTableHeader,HtmlTableFooter和HtmlTableBody部分是以行分组的。其最多只能有一个头和一个页脚,但可能会有不止一个的身体。行中包含的都可以通过getRows()访问。

       final HtmlTableHeader header = table.getHeader();final List<HtmlTableRow> headerRows = header.getRows();final HtmlTableFooter footer = table.getFooter();final List<HtmlTableRow> footerRows = footer.getRows();for(final HtmlTableBody body : table.getBodies()) {final List<HtmlTableRow> rows =body.getRows();...}

每个表格可以选择性的有一个标题,用于说明
final String caption = table.getCaptionText();

5.使用Frames

例一

可以通过HtmlPage.getFrames()获取页面里面的<frame>或<iframe>元素,假设你有下面的页面。

<html><body><iframe src="two.html"></body></html>

你可以使用下面的代码获取上述页面的内容。

       final List<FrameWindow> window = page.getFrames();final HtmlPage pageTwo = (HtmlPage)window.get(0).getEnclosedPage();

例二

这个例子是以API文档的导航页作为需要的页面的。

       final WebClient client = new WebClient();final HtmlPage mainPage = client.getPage("http://htmlunit.sourceforge.net/apidocs/index.html");//为了获取页面的第一个frame(在左上角)并点击第六个连接。final HtmlPage packageListPage = (HtmlPage)mainPage.getFrames().get(0).getEnclosedPage();packageListPage.getAnchors().get(5).click();//为了获取页面中名为packageFrame的frame(在左下)并点击第二个链接。final HtmlPage pakcagePage = (HtmlPage)mainPage.getFrameByName("packageFrame").getEnclosedPage();pakcagePage.getAnchors().get(1).click();//为了获取页面中名为classFrame的frame(在右边)。final HtmlPage classPage = (HtmlPage)mainPage.getFrameByName("classFrame").getEnclosedPage();

使用 Windows

所有页面都包含在WebWindows对象里,在里面一个顶层的窗口( TopLevelWindows)代表一个实际的浏览器,一个HtmlFrame代表一个<frame>元素,或者一个HtmlInlineFrame代表一个<Iframe>元素。
当一个WebClient第一次实例化,一个顶层窗口就被创建。你可认为他就是通过浏览器展示的第一个窗口。调用WebClient.getPage(WebWindow, WebRequest)方法将会加载一个新的页面进入该窗口。
JavaScript的open() 方法在加载页面到其他窗口时被使用到,新的WebWindows对象通过这种方法将会被自动创建。

WebWindowsEvents

如果你想在窗口被创建或者页面加载时得到通知,你需要为WebClient注册一个WebWindowsListener,可以通过方法WebClient.addWebWindowListener(WebWindowListener)实现。当一个窗口被JavaScript或者通过WebClient打开时,一个WebWindowsEvent将被触发,该事件将会传入WebWindowListener.webWindowOpened(WebWindowEvent)方法。注意,如果这时窗口没有任何内容加载,在实践中新和旧的页面将被置为null。如果在创建窗口时指定了URL,页面将会被加载而且其他事件将被触发。
一个新的页面被加载到指定窗口时,一个WebWindowsEvent将被触发件将会传入WebWindowListener.webWindowContentChanged(WebWindowEvent)方法。

7.使用JavaScript

介绍

我们收到的常见问题是,我们该怎样测试我们的JavaScript。使用JavaScript真的没有什么特别的,它是一个自动的过程,你只需要用getPage()获取页面,找到元素,然后调用click(),最后检查结果就可以了。复杂的JavaScript测试的库包含在HtmlUnit的测试基地里,你可以通过链接http://sourceforge.net/p/htmlunit/code/HEAD/tree/找到,也许你可以发现有用的想法。

使用Document.write()

例如说,我们有一个页面包含JavaScript,脚本动态向页面写内容。下面的html文本将动态生成五个文本域并将其插入表格。被一个文本域通过追加的索引到字符串获得唯一的名字。

<html><head><title>Tablesample</title></head><body><form action='/foo' name='form1'><table id="table1"><scripttype="text/javascript">for (i = 1; i <= 5; i++) {document.write("<tr><td>" + i+ "</td><td><input name='textfield" + i+ "' type='text'></td></tr>");}</script></table></form></body></html>

我们可能想测试五个文本域是否被创建,我们可以这样做。

       @Testpublicvoid documentWrite() throws Exception {final WebClient webClient = new WebClient();final HtmlPage page =webClient.getPage("http://myserver/test.html");final HtmlForm form =page.getFormByName("form1");for (int i = 1; i <= 5; i++) {final String expectedName ="textfield" + i;Assert.assertEquals("text",form.<HtmlInput>getInputByName(expectedName).getTypeAttribute());}}

我们可能想检查差一错误(off-by-one,比指定范围大一或小一)错误,确保不会创建textfield0”或textfield6。尝试获取不存在的元素将会抛出异常,因此我们可以添加这些在前期测试的尾部。

    try{form.getInputByName("textfield0");fail("Expected anElementNotFoundException");}catch(final ElementNotFoundException e) {// Expected path}try{form.getInputByName("textfield6");fail("Expected anElementNotFoundException");}catch(final ElementNotFoundException e) {// Expected path}

等待警告

通常你想查看JavaScript触发的警告。

 <html><head><title>Alertsample</title></head><body onload='alert("foo");'></body></html>

AlertHandler可以跟踪警告,无论什么时候,当JavaScript的alert函数被调用时,AlertHandler将被调用。在下面的测试中,我们注册一个警告处理者(Alert Handler),通过它将所有警告消息存入列表。当页面加载完全,我们比较收集到的警告消息列表和预期的消息的列表,确保他们一致。

       @Testpublicvoid alerts() throws Exception {final WebClient webClient = new WebClient();         final List collectedAlerts = new ArrayList();webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));           // 因为我们没有实际操作页面,我们不分配给一个变量——这足以知道它加载。//可以将url改为你有的页面链接webClient.getPage("http://tciludev01/test.html");final List expectedAlerts =Collections.singletonList("foo");Assert.assertEquals(expectedAlerts,collectedAlerts);}

提示,确认和状态栏信息

提示,确认和状态栏消息的处理和警告的处理一样。你注册一个适当的处理者(handler),当那些方法被调用时他将获得通知。查看WebClient.setPromptHandler(),WebClient.setConfirmHandler() 和WebClient.setStatusHandler() 的详细信息可以参考API Doc。

事件处理

大多数的事件处理已被实现,onload, onclick, ondblclick,onmouseup, onsubmit, onreadystatechange, …它们将会在适当的时候被触发就像在真正的浏览器那样。如果你测试不支持的事件,你可以直接通过ScriptEngine调用它。注意,脚本引擎(ScriptEngine)是公共可访问的,我们不推荐你直接使用,除非你没有其他选择。友好的用户界面允许用户点击元素和改变焦点。

8. 使用ActiveX

动机

虽然HtmlUnit是纯java实现的仿真浏览器,但是,在某些特定的情况下,特殊平台的功能需求集成其他的库,ActiveX就是他们中的一个。
Windows的IE可以运行任意的ActiveX组件(如果是用户信任的网站,可有目的的降低安全等级)。HtmlUnit或者IE都没有任何控制ActiveX的运行行为,所以当你是用该功能时你必须小心。

Jacob

当前的实现是依赖Jacob的,由于它有dll的依赖项,所以在Maven仓库没有更新。依赖项是可选的,例如,Jacob jar对于编译或平时HtmlUnit的使用的无须的。

为了使用Jacob,需要将Jacob.jar添加到CLASSPATH,而且将.dll添加到java.library.path(jdk安装目录的bin或lib文件夹),确保下面的代码可以工作。

       final ActiveXComponent activeXComponent = new ActiveXComponent("InternetExplorer.Application");final boolean busy = activeXComponent.getProperty("Busy").getBoolean();System.out.println(busy);

允许HtmlUnit使用ActiveX

唯一需要做的事就是设置WebClient的属性。

webClient.getOptions().setActiveXNative(true);

9.写在最后

本文是在参考HtmlUnit官网内容的基础上,翻译修改而来,由于能力有限,欢迎指正其中存在的问题。
本文只作为HtmlUnit的入门级教程,看完本文相信你也对HtmlUnit有了初步的了解,如需进一步研究,建议读者自行参考它的开发文档。另外,由于HtmlUnit面向的对象为Html,所以对它的使用需要一定的Html基础,如需参考Html的知识建议参考参考文献[3]。最后,要想真正提升知识的理解需要多动手的同时,好要善于使用网络资源,学会查找资料。
最后希望看到本文的读者可以一起进步。

10.参考文献

[1] sourceforge.net. HtmlUnit[Z].http://htmlunit.sourceforge.net/
[2] 百度百科.HtmlUnit[Z].http://baike.baidu.com/view/3724597.htm
[3] w3school.HTML[Z].http://www.w3school.com.cn/html/index.asp
[4] w3school.XPath[Z].http://www.w3school.com.cn/xpath/

【转】HtmlUnit入门教程相关推荐

  1. HtmlUnit入门教程

    作者:吴香礼 Email:wxl901018@163.com QQ:1060394242 本文禁止用于商业用途 1.     简介 htmlunit是一款开源的java页面分析工具,读取页面后,可以有 ...

  2. Kafka入门教程与详解

    1 Kafka入门教程 1.1 消息队列(Message Queue) Message Queue消息传送系统提供传送服务.消息传送依赖于大量支持组件,这些组件负责处理连接服务.消息的路由和传送.持久 ...

  3. 【CV】Pytorch一小时入门教程-代码详解

    目录 一.关键部分代码分解 1.定义网络 2.损失函数(代价函数) 3.更新权值 二.训练完整的分类器 1.数据处理 2. 训练模型(代码详解) CPU训练 GPU训练 CPU版本与GPU版本代码区别 ...

  4. python tornado教程_Tornado 简单入门教程(零)——准备工作

    前言: 这两天在学着用Python + Tornado +MongoDB来做Web开发(哈哈哈这个词好高端).学的过程中查阅了无数资料,也收获了一些经验,所以希望总结出一份简易入门教程供初学者参考.完 ...

  5. python向量计算库教程_NumPy库入门教程:基础知识总结

    原标题:NumPy库入门教程:基础知识总结 视学算法 | 作者 知乎专栏 | 来源 numpy可以说是 Python运用于人工智能和科学计算的一个重要基础,近段时间恰好学习了numpy,pandas, ...

  6. mysql query browswer_MySQL数据库新特性之存储过程入门教程

    MySQL数据库新特性之存储过程入门教程 在MySQL 5中,终于引入了存储过程这一新特性,这将大大增强MYSQL的数据库处理能力.在本文中将指导读者快速掌握MySQL 5的存储过程的基本知识,带领用 ...

  7. python tensorflow教程_TensorFlow入门教程TensorFlow 基本使用T

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 TensorFlow入门教程 TensorFlow 基本使用 TensorFlow官方中文教程 TensorFlow 的特点: 使用图 (graph) 来 ...

  8. air调用java,AIR2.0入门教程:与Java应用交互

    在之前的一篇文章中,我介绍了如何使用AIR2.0新增的NativeProcess类与本地进程进行交互和通讯,在那个例子里面我们使用了C++ 的代码,实际上只要是基于命令行的标准输入输出,AIR2.0的 ...

  9. 【Arduino】开发入门教程【一】什么是Arduino

    Arduino Arduino 是一款便捷灵活.方便上手的开源电子原型平台,包含硬件(各种型号的arduino板)和软件(arduino IDE).它适用于艺术家.设计师.爱好者和对于"互动 ...

最新文章

  1. .Net Core下发送WebRequest请求的两种方式
  2. 【 C 】经典抽象数据类型(ADT)之内存分配
  3. JUnit 5 –参数化测试
  4. Perl迎来25岁生日
  5. python中右对齐_python中如何右对齐-问答-阿里云开发者社区-阿里云
  6. php文本框显示ip,php实现图形显示Ip地址的代码及注释_PHP教程
  7. WPS显示无法创建对象,请确认对象已在系统注册表中注册
  8. Stereo Matching文献笔记之(九):经典算法Semi-Global Matching(SGM)之神奇的HMI代价计算~
  9. matlab中phantom函数,matlab官网上下的phantom3d不对
  10. 关于LANP的相关常识题
  11. python psutil 汇总 tcy
  12. 需要u3d资源吗,免费送哦
  13. PHP 蚂蚁芝麻信用分接口
  14. 【Minecraft开服教程】使用 MCSM 面板一键搭建我的世界服务器,并内网穿透公网远程联机
  15. 单片机计数器实验代码c语言,单片机计数器功能实验程序
  16. 计算机基础知识比赛主持稿,计算机基础技能大赛.doc
  17. java岗位面试英文自我介绍,面试外企英文自我介绍
  18. 默克尔树(Merkle Tree)总结
  19. 本科硕士毕业论文格式自查工具方法分享
  20. html em用法,中文Web设计HTML的em标记使用

热门文章

  1. 贡献15本超级经典Android教程,都是pdf完整版的
  2. C++实现求小于n的所有素数
  3. 数字图像处理实验二 图像变换
  4. 详解SimpleDateFormat
  5. 视频播放不了?如何修复视频文件?
  6. 鸿蒙系统体验效果,搭载鸿蒙操作系统的智能家居,实现一键体验全场景效果
  7. 程序员求职攻略(《程序员面试笔试宝典》)之面试笔试技巧?
  8. 百度前端技术学院-斌斌学院-任务五
  9. 数据湖和数据仓库的区别?
  10. 2048之军衔篇 反馈 有事留言