jsoup 是一款基于 Java 语言的 HTML 请求及解析器,可直接请求某个 URL 地址、解析 HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM、CSS 以及类似于 jQuery 的操作方法来取出和操作数据。
jsoup 的下载
首先,我们在 Eclipse 中创建 Maven 工程,并配置 pom.xml 文件。基于下面的配置文件便可以下载 jsoup 对应的 Jar 包(这里使用的版本是1.11.3)。
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
jsoup 请求 URL
给定一个 URL,可通过 jsoup 工具请求得到 HTML 文件。例如,我们请求 W3school 的某一页面(比如:http://www.w3school.com.cn/b.asp)。首先,采用网页浏览器抓包分析该网址,抓包结果如下:
接着,便可以使用 jsoup 提供的 connect(String url) 方法创建一个新的 Connection,并通过 get() 获取网页对应的 HTML 文件,具体如下:
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").get();
System.out.println(doc);
通过这两行程序便可以在 Eclipse 控制台输入该网址对应的 HTML 文件(由于该 HTML 文件的内容过长,,在此我就不展示了)。另外,jsoup 也可以使用 Post() 方法请求网页内容,使用如下:
//可以看到该网址通过两种方法都能请求到内容
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").post();
System.out.println(doc);
另外,在爬取一些网站时,可能需要添加头信息。添加头信息可以很好地伪装我们的爬虫,使得该爬虫更像是浏览器访问,从而降低了爬虫被封的风险。同样,针对 W3school 的网址为例,通过抓包获取 Request Headers,并将这些头信息添加到程序中,具体程序如下:
Connection connect = Jsoup.connect("http://www.w3school.com.cn/b.asp"); //获取请求连接
//使用Map集合存储头信息
Map<String, String> header = new HashMap<String, String>();
header.put("Host", "www.w3school.com.cn");
header.put("User-Agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36");
header.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
header.put("Accept-Language", "zh-cn,zh;q=0.5");
header.put("Accept-Encoding", "gzip, deflate");
header.put("Cache-Control", "max-age=0");
header.put("Connection", "keep-alive");
//添加头信息
Connection conheader = connect.headers(header);
//使用get()请求页面内容
Document document = conheader.get();
//输出页面内容
System.out.println(document);
同时,jsoup 还提供了解决特殊请求的方法,例如连接超时问题(Connect Timeout)。具体如下:
//设置延迟时间,5000指5秒
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
System.out.println(doc);
jsoup 解析 HTML 文件
jsoup 另外一个重要用途是解析 HTML 文件,在该小节主要讲解其用法。
在开始用法之前,必须弄清 jsoup 中的 Node、Element、Document 的相关概念及区别,防止因概念混淆而导致乱用错用。
Node(节点):HTML 中所包含的内容都可以看成一个节点。节点有很多种类型:属性节点(Attribute)、注释节点(Note)、文本节点(Text)、元素节点(Element)等。解析 HTML 内容的过程,其实就是对节点操作的过程。
Element(元素):元素是节点的子集,所以一个元素也是一个节点。
Document(文档):指整个 HTML 文档的源码内容。
下面将介绍 jsoup 解析 HTML 文件的具体用法。
解析静态 HTML 文件
给定一个 HTML 字符串,jsoup 使用 Jsoup.parse(String html) 的方法,将 String 类型的 HTML 转化成了 Document 类型,并可以使用 select 选择器定位要解析的内容。具体案例如下:
//需要解析的HTML文本
String html = "<html><body><div id=\"w3school\"> <h1>浏览器脚本教程</h1> <p><strong>从左侧的菜单选择你需要的教程!</strong></p> </div>"
+ "<div> <div id=\"course\"> <ul> <li><a href=\"/js/index.asp\" title=\"JavaScript 教程\">JavaScript</a></li> </ul> </div> </body></html>";
Document doc = Jsoup.parse(html); //转化成Document
Element element = doc.select("div[id=w3school]").get(0); //获取Element
String text1 = element.select("h1").text(); //从Element提取内容(抽取一个Node对应的信息)
String text2 = element.select("p").text(); //从Element提取内容(抽取一个Node对应的信息)
System.out.println("输出解析的元素内容为:");
System.out.println(element);
System.out.println("抽取的文本信息为:");
System.out.println(text1 + "\t" + text2);
其中,在 Eclipse 控制台输出的元素 Element 以及提取的节点内容结果如下:
jsoup 解析 URL 加载的 Document
给定一个 URL,jsoup 可首先请求 URL,获取 HTML 文件,在采用 jsoup 中的 select 选择器定位并解析内容。具体案例如下,其输出结果与解析静态 HTML 文件是一致的。
//获取URL对应的HTML内容
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
//select选择器解析内容
Element element = doc.select("div#w3school").get(0); //获取Element,这里相当于div[id=w3school]
String text1 = element.select("h1").text(); //从Element提取内容(抽取一个Node对应的信息)
String text2 = element.select("p").text(); //从Element提取内容(抽取一个Node对应的信息)
System.out.println("输出解析的元素内容为:");
System.out.println(element);
System.out.println("抽取的文本信息为:");
System.out.println(text1 + "\t" + text2);
jsoup 使用中的遍历
通过 jsoup 选择多个 Element 元素,并对这些元素进行遍历,便可以解析每个元素中对应的内容。为了读者更好的操作,我仍然以 W3school 的页面为例(即:http://www.w3school.com.cn/b.asp),页面内容如下图所示:
在上图中,我想要解析的内容是每个课程表的标题信息,以及每个课程表对应的 URL 信息。首先,通过抓包分析定位到我要解析内容,如下:
<div id="course">
<ul>
<li>
<a href="/js/index.asp" title="JavaScript 教程">JavaScript</a></li>
<li>
<a href="/htmldom/index.asp" title="HTML DOM 教程">HTML DOM</a></li>
<li>
<a href="/jquery/index.asp" title="jQuery 教程">jQuery</a></li>
<li>
<a href="/ajax/index.asp" title="AJAX 教程">AJAX</a></li>
<li>
<a href="/json/index.asp" title="JSON 教程">JSON</a></li>
<li>
<a href="/dhtml/index.asp" title="DHTML 教程">DHTML</a></li>
<li>
<a href="/e4x/index.asp" title="E4X 教程">E4X</a></li>
<li>
<a href="/wmlscript/index.asp" title="WMLScript 教程">WMLScript</a></li>
</ul>
</div>
分析上面的 HTML 片段结构发现,要解析的课程列表内容在标签 a 中,标签 a 在标签 li 中,li 标签在 div [id=course] 中。针对这种结构,可以使用 jsoup 中的遍历很好地解析出每个内容。具体代码如下:
//获取URL对应的HTML内容
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
//层层定位到要解析的内容,可以发现包含多个li标签
Elements elements = doc.select("div[id=course]").select("li");
//遍历每一个li节点
for (Element ele : elements) {
String title = ele.select("a").text(); //.text()为解析标签中的文本内容
String course_url = ele.select("a").attr("href"); //.attr(String)表示获取标签内某一属性的内容
System.out.println("课程的标题为:" + title + "\t对应的URL为" + course_url);
}
通过上述程序便能够解析出我想要的内容,程序运行结果如下:
另外,jsoup 中的 Elements 对象,提供了一系列查找元素的方法,具体包括:
getElementById(String id) //通过id查找
getElementsByTag(String tag) //通过标签查找
getElementsByClass(String className) //通过类名查找
getElementsByAttribute(String key) (and related methods) //通过属性查找
Element siblings: siblingElements(), firstElementSibling(), lastElementSibling(); nextElementSibling(), previousElementSibling() //获取兄弟节点
Graph: parent(), children(), child(int index) //获取节点的父节点,子节点
部分方法使用案例如下:
//获取URL对应的HTML内容
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
//层层定位到要解析的内容,可以发现包含多个li标签
System.out.println("通过id提取的结果为:" + doc.getElementById("course").text()); //通过id定位,并获取文本
System.out.println("通过tag提取的结果为:" + doc.getElementById("course").getElementsByTag("a").text());
System.out.println("通过attr提取的结果为:" + doc.getElementById("course").getElementsByAttribute("href").text());
Elements elements = doc.getElementsByClass("browserscripting"); //获取HTML文档中指定class名的所有元素
System.out.println("通过class提取的元素为:" + elements);
jsoup 选择器的使用
从上面一些案例可以发现,jsoup 的强大解析功能,多数依赖于选择器的使用,其使用的基本语法如下:
Element.select(String selector);
Elements.select(String selector);
我以具体案例的方式进行操作的实战说明。
//获取URL对应的HTML内容
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
//[attr=value]: 利用属性值来查找元素,例如[id=course]; 通过tagname: 通过标签查找元素,比如:a
System.out.println(doc.select("[id=course]").select("a").get(0).text());
//fb[[attr=value]:利用标签属性联合查找
System.out.println(doc.select("div[id=course]").select("a").get(0).text());
//#id: 通过ID查找元素,例如,#course
System.out.println(doc.select("#course").select("a").get(0).text());
//通过属性属性查找元素,比如:[href]
System.out.println(doc.select("#course").select("[href]").get(0).text());
//.class通过class名称查找元素
System.out.println(doc.select(".browserscripting").text());
//[attr^=value], [attr$=value], [attr*=value]利用匹配属性值开头、结尾或包含属性值来查找元素(很常用的方法)
System.out.println(doc.select("#course").select("[href$=index.asp]").text());
//[attr~=regex]: 利用属性值匹配正则表达式来查找元素,*指匹配所有元素
System.out.println(doc.select("#course").select("[href~=/*]").text());
jsoup 使用总结
jsoup 主要用来解析 HTML 内容,解析主要利用选择器和遍历操作,层层选择自己需要的信息