java有httpclient等工具,可以模拟进行一些web操作,但一些逻辑是在前端js中执行的,此时httpclient就比较困难了。
此时可以考虑使用HtmlUnit,模拟出一个浏览器,全程在浏览器中操作。
本文以 在百度中输入搜索关键字->点击“百度一下” 按钮->打印搜索结果 这一过程为例,演示HtmlUnit的使用
首先,我们要把上面的过程再代码化一点,F12打开开发者工具,检查发现输入框的id是kw,查询按钮的id是su。
而点击查询后,查询结果是写在这样的div里:
<div data-tools="{'title': "xxx技术博客_51CTO博客",'url': xxx;}" aria-hidden="true"></div>
因此,上述过程可以描述为
1、打开百度首页
2、查找id为kw的元素,写入查询关键字
3、查找id为su的元素,点击它
4、在页面中查找div标签并打印data-tools属性。
依照此过程,我们就开始写代码了:
引入maven依赖
<dependency><groupId>net.sourceforge.htmlunit</groupId><artifactId>htmlunit</artifactId><version>2.60.0</version></dependency>
按流程写代码
package org.example;import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
import com.gargoylesoftware.htmlunit.util.Cookie;
import org.apache.commons.lang3.StringUtils;import java.util.LinkedList;
import java.util.List;
import java.util.Set;public class Main {public static void main(String[] args) {// 创建一个模拟 Chrome 浏览器的 WebClient 实例final WebClient webClient = new WebClient(BrowserVersion.CHROME);// 配置 WebClientwebClient.getOptions().setThrowExceptionOnScriptError(false);//当JS执行出错的时候是否抛出异常, 这里选择不需要webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);//当HTTP的状态非200时是否抛出异常, 这里选择不需要webClient.getOptions().setActiveXNative(false);webClient.getOptions().setCssEnabled(false);//是否启用CSS, 因为不需要展现页面, 所以不需要启用webClient.getOptions().setJavaScriptEnabled(true); //很重要,启用JSwebClient.setAjaxController(new NicelyResynchronizingAjaxController());//很重要,设置支持AJAXHtmlPage page = null;try {// 访问百度主页page = webClient.getPage("https://www.baidu.com/");// 找到搜索输入框HtmlTextInput textField;do {textField = (HtmlTextInput) page.getElementById("kw");if (null != textField) {break;}// 未找到说明页面还没加载完,继续等待页面加载完成webClient.waitForBackgroundJavaScript(1000);} while (true);// 输入查询文本textField.setValueAttribute("HtmlUnit自动测试");// 找到提交按钮并点击HtmlSubmitInput button = (HtmlSubmitInput) page.getElementById("su");page = button.click();//解析页面结果 这里如果百度的页面改了,需要做相应的调整List<String> res = new LinkedList<>();do {// 等待异步 JS 执行完成webClient.waitForBackgroundJavaScript(1000);// 提取结果页面中的<div data-tools="{'title': xxx">元素,也就是百度的检索结果关键元素for (DomElement div : page.getElementsByTagName("div")) {String text = div.getAttribute("data-tools");if (StringUtils.isNotBlank(text)) {res.add(text);}}} while (res.isEmpty());//res非空说明解析到结果了for (String s : res) {System.out.println(s);}System.out.println("--------------------------------------------------");// 获取并打印 cookiesSet<Cookie> cookies = webClient.getCookieManager().getCookies();System.out.println("Cookies:");for (Cookie cookie : cookies) {System.out.println("Name: " + cookie.getName() + ", Value: " + cookie.getValue());}} catch (Exception e) {e.printStackTrace();} finally {webClient.close();}}}
运行得到结果。
最后提醒,本文只是以此为例介绍HtmlUnit的使用,请读者在应用中注意遵纪守法,各网站基本都有反爬虫机制,爬虫写得好,牢饭吃到饱。HtmlUnit更应该用到页面自动化测试等场景,而不是打擦边球去写爬虫。