标准的 Java DOM 解析器(如 javax.xml.parsers.DocumentBuilder)是为设计良好、格式规范的 XML 文档设计的,而 HTML 通常结构松散、可能包含不闭合的标签、大小写不敏感等,直接使用标准的 XML DOM 解析器来解析 HTML 很容易失败或抛出异常。

我们需要使用专门为 HTML 设计的 DOM 解析器,目前最流行和推荐的库是 Jsoup,它非常强大、易用,并且专门处理了 HTML 的各种不规范情况。
下面我将分两部分介绍:
- 推荐方案:使用 Jsoup(强烈推荐)
- 不推荐方案:使用标准 Java DOM(仅用于理解原理)
使用 Jsoup(强烈推荐)
Jsoup 是一个开源的 Java HTML 解析库,它提供了非常直观的 API,类似于 jQuery 的 CSS 选择器,非常适合从网页中提取和操作数据。
添加 Jsoup 依赖
如果你使用 Maven,在 pom.xml 中添加以下依赖:

<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version> <!-- 请使用最新版本 -->
</dependency>
如果你使用 Gradle,在 build.gradle 中添加:
implementation 'org.jsoup:jsoup:1.17.2' // 请使用最新版本
Jsoup 核心功能与示例
假设我们有如下 HTML 内容:
<!DOCTYPE html>
<html>
<head>Jsoup 示例</title>
</head>
<body>
<h1>欢迎来到我的网站</h1>
<div class="content">
<p>这是一个段落。</p>
<p>这是另一个段落,包含一个 <a href="https://jsoup.org/" id="jsoup-link">链接</a>。</p>
</div>
<ul>
<li>项目 1</li>
<li>项目 2</li>
</ul>
</body>
</html>
示例 1:解析 HTML 字符串并获取标题
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
public class JsoupParseFromString {
public static void main(String[] args) {
String html = "<html><head><title>Jsoup 示例</title></head><body><p>一个段落。</p></body></html>";
// 1. 解析 HTML 字符串,返回一个 Document 对象
Document doc = Jsoup.parse(html);
// 2. 通过标签名获取元素 (获取 <title> 标签)
Element titleElement = doc.title(); // 有便捷方法直接获取 title
// 或者 Element titleElement = doc.select("title").first();
System.out.println("网页标题是: " + titleElement.text()); // 输出: 网页标题是: Jsoup 示例
}
}
示例 2:使用 CSS 选择器提取数据
这是 Jsoup 最强大的功能,选择器语法和 CSS 一样。
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.List;
public class JsoupSelectExample {
public static void main(String[] args) {
String html = "<html><head><title>Jsoup 示例</title></head>" +
"<body><h1>欢迎来到我的网站</h1>" +
"<div class='content'><p>这是一个段落。</p><p>这是另一个段落。</p></div>" +
"<ul><li>项目 1</li><li>项目 2</li></ul></body></html>";
Document doc = Jsoup.parse(html);
// a. 获取所有 <li> 元素
Elements listItems = doc.select("li");
System.out.println("--- 所有列表项 ---");
for (Element li : listItems) {
System.out.println("列表项: " + li.text());
}
// b. 获取 class 为 'content' 的 div 下的所有 <p> 元素
Elements paragraphsInContent = doc.select("div.content p");
System.out.println("\n--- content div 中的段落 ---");
for (Element p : paragraphsInContent) {
System.out.println("段落: " + p.text());
}
// c. 获取 id 为 'jsoup-link' 的 <a> 标签的 href 属性
// 假设 HTML 中有这个链接
String htmlWithLink = "<a href='https://jsoup.org/' id='jsoup-link'>链接</a>";
Document docWithLink = Jsoup.parse(htmlWithLink);
Element link = docWithLink.select("#jsoup-link").first(); // #id 选择器
String linkUrl = link.attr("href"); // .attr() 方法获取属性
System.out.println("\n--- 链接信息 ---");
System.out.println("链接地址: " + linkUrl);
System.out.println("链接文本: " + link.text());
// d. 组合选择器:获取 div.content 中的第一个 <a> 标签
// 假设 HTML 中有这个结构
String complexHtml = "<div class='content'><p>文本和 <a href='#'>一个链接</a>。</p></div>";
Document complexDoc = Jsoup.parse(complexHtml);
Element firstLink = complexDoc.select("div.content a").first();
System.out.println("\n--- 第一个链接 ---");
System.out.println(firstLink);
}
}
示例 3:从 URL 获取并解析 HTML
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
public class JsoupFromUrl {
public static void main(String[] args) {
try {
// 从 URL 解析 HTML,可以设置超时时间和 User-Agent
Document doc = Jsoup.connect("https://jsoup.org/")
.userAgent("Mozilla/5.0")
.timeout(10000)
.get();
// 获取页面标题
String title = doc.title();
System.out.println("页面标题: " + title);
// 获取页面的所有文本内容
String text = doc.body().text();
System.out.println("\n页面文本内容 (前100字符):");
System.out.println(text.length() > 100 ? text.substring(0, 100) + "..." : text);
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例 4:从文件加载 HTML
假设你有一个 index.html 文件。

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
public class JsoupFromFile {
public static void main(String[] args) {
try {
File input = new File("path/to/your/index.html");
Document doc = Jsoup.parse(input, "UTF-8"); // 指定编码
System.out.println("从文件解析的标题: " + doc.title());
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用标准 Java DOM(不推荐,仅作了解)
如果你因为某些特殊原因(如不能引入第三方库)必须使用 Java 自带的 DOM 解析器,你需要先想办法将 HTML 转换为格式良好的 XML,这通常非常困难,因为 HTML 的不规范之处太多,一个折中的办法是移除或修复一些常见的“坏”标签(如 <br>, <img> 等),但这不是通用的解决方案。
以下是一个演示,它很可能失败,除非你的 HTML 文件格式非常完美。
准备工作
假设你有一个格式完美的 my_page.html 文件,它看起来像一个 XML 文件。
<!-- my_page.html -->
<html>
<head>标准 DOM 示例</title>
</head>
<body>
<h1>你好</h1>
<p>这是一个段落。</p>
</body>
</html>
Java 代码
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
public class JavaDomParser {
public static void main(String[] args) {
// 1. 创建 DocumentBuilderFactory 和 DocumentBuilder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. 解析 XML (HTML) 文件,得到 Document 对象
// 注意:HTML 不规范,这里会抛出 SAXException
Document document = builder.parse(new File("my_page.html"));
// 3. 获取文档的根元素
Element rootElement = document.getDocumentElement();
System.out.println("根元素是: " + rootElement.getNodeName());
// 4. 获取所有 <p> 标签
NodeList pList = document.getElementsByTagName("p");
System.out.println("\n找到 " + pList.getLength() + " 个 <p> 标签:");
for (int i = 0; i < pList.getLength(); i++) {
Element pElement = (Element) pList.item(i);
// 获取 <p> 标签内的文本内容
// textContent 可以获取标签及其所有子标签的文本
System.out.println(" 段落 " + (i + 1) + ": " + pElement.getTextContent());
}
// 5. 获取 <title> 标签的文本
NodeList titleList = document.getElementsByTagName("title");
if (titleList.getLength() > 0) {
Element titleElement = (Element) titleList.item(0);
System.out.println("\n页面标题: " + titleElement.getTextContent());
}
} catch (ParserConfigurationException e) {
System.err.println("解析器配置错误: " + e.getMessage());
} catch (SAXException e) {
System.err.println("解析 HTML 时发生 SAX 错误 (通常是 HTML 格式不正确): " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("读取文件时发生 IO 错误: " + e.getMessage());
}
}
}
总结与对比
| 特性 | Jsoup (推荐) | 标准 Java DOM (不推荐) |
|---|---|---|
| 易用性 | 极高,类似 jQuery 的选择器,非常直观 | 低,需要遍历节点,代码冗长 |
| 健壮性 | 极高,能完美处理不规范、破损的 HTML | 极低,对格式要求严格,HTML 稍有不规范就会报错 |
| 功能 | 强大,除了解析,还支持数据修改、输出 HTML、清理等 | 基本,仅支持读取和简单遍历 |
| 依赖 | 需要引入第三方库 (Maven/Gradle) | 无需额外依赖,JDK 自带 |
| 适用场景 | 所有 HTML 解析需求,如网络爬虫、数据抓取 | 极少数情况,如解析一个已知格式完美且无法引入外部库的类 XML 文件 |
对于任何 Java 项目中需要解析 HTML 的场景,都应该毫不犹豫地选择 Jsoup,它解决了标准 DOM 解析器处理 HTML 时的一切痛点,提供了现代化、高效且强大的 API,能让你用最少的代码完成最多的工作。
