Jsoup 基于标准的 DOM 树模型,将 HTML 文档解析为节点(Node) 层级结构,核心是 父子(Parent-Child)、子父(Child-Parent)、祖先-后代、兄弟(Sibling) 四种层级关系。下面从核心概念、API 方法、选择器语法、代码示例四方面详细介绍。
一、核心层级概念
Jsoup 的节点继承关系:
Document(文档根节点)→ Element(HTML 元素)→ Node(基础节点)。
1. 父子 / 子父(最核心)
- 父节点(Parent):直接包含当前节点的上一级节点。
- 子节点(Child):被父节点直接包含的下一级节点。
- 直接子节点:仅一层嵌套(
parent > child)。 - 后代节点(Descendant):子节点 + 孙子节点 + 所有下层节点(
parent child)。
2. 祖先 / 后代
- 祖先(Ancestor):父节点、祖父节点…直到根节点(
html/Document)。 - 后代(Descendant):当前节点下所有层级的节点。
3. 兄弟节点(Sibling)
- 拥有同一个父节点的同级节点。
- 相邻兄弟、前一个、后一个、所有兄弟。
二、父子 / 子父 常用 API(Element 方法)
1. 向上查找(子 → 父 / 祖先)
Element parent()
获取直接父元素(最常用)。Node parentNode()
获取父节点(含文本节点、注释节点)。Element ancestors()
获取所有祖先元素(从父到根)。Element closest(String cssQuery)
向上查找最近匹配选择器的祖先(常用)。
2. 向下查找(父 → 子 / 后代)
Elements children()
获取所有直接子元素(不含文本/注释)。Element child(int index)
获取第 index 个直接子元素(从 0 开始)。Elements select(String cssQuery)
查找所有后代中匹配选择器的元素(最常用)。Elements selectDirect(String cssQuery)
仅查找直接子元素中匹配的。
3. 同级(兄弟)
Element nextElementSibling():下一个兄弟Element previousElementSibling():上一个兄弟Elements siblingElements():所有兄弟
三、层级 CSS 选择器(最实用)
| 语法 | 含义 | 示例 |
|---|---|---|
E F | 后代:E 下所有 F(多层) | div p → div 下所有 p |
E > F | 直接子:E 直接子元素 F(一层) | div > p → 仅直属 p |
E + F | 相邻兄弟:E 后紧跟的 F | h1 + p → h1 后第一个 p |
E ~ F | 通用兄弟:E 之后所有同级 F | h1 ~ p → h1 后所有 p |
四、完整代码示例
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class JsoupHierarchyDemo {
public static void main(String[] args) {
String html = """
<html>
<body>
<div id="parent">
<p class="child direct">直接子P1</p>
<section>
<p class="child descendant">孙子P2(后代)</p>
</section>
<p class="child direct">直接子P3</p>
</div>
</body>
</html>
""";
Document doc = Jsoup.parse(html);
Element parent = doc.getElementById("parent");
// ----------------------
// 1. 父 → 子(向下)
// ----------------------
// 所有直接子元素
Elements directChildren = parent.children();
System.out.println("直接子元素数量:" + directChildren.size()); // 3:p, section, p
// 第0个直接子元素
Element firstChild = parent.child(0);
System.out.println("第一个直接子:" + firstChild.text()); // 直接子P1
// 所有后代p(含孙子)
Elements allDescendantP = parent.select("p");
System.out.println("所有后代p:" + allDescendantP.size()); // 3
// 仅直接子p
Elements directChildP = parent.select("> p");
System.out.println("直接子p:" + directChildP.size()); // 2
// ----------------------
// 2. 子 → 父(向上)
// ----------------------
Element grandsonP = doc.select("section > p").first();
// 直接父
Element directParent = grandsonP.parent();
System.out.println("P2的直接父:" + directParent.tagName()); // section
// 最近的div祖先
Element closestDiv = grandsonP.closest("div");
System.out.println("最近div:" + closestDiv.id()); // parent
// ----------------------
// 3. 兄弟
// ----------------------
Element p1 = parent.child(0);
Element nextSib = p1.nextElementSibling();
System.out.println("P1的下一个兄弟:" + nextSib.tagName()); // section
}
}
五、常见误区
-
children()≠select("*")children():仅直接子元素(Element)。select("*"):所有后代元素(多层)。
-
parent()与parentNode()区别parent():返回Element(仅标签元素)。parentNode():返回Node(可能是文本/注释)。
-
>必须直接包含div > p不匹配<div><span><p></p></span></div>(p 不是 div 直接子)。
六、总结
- 父子:
parent()/children()/child(int) - 后代:
select("E F")(全层级) - 直接子:
select("E > F")(仅一层) - 向上找祖先:
closest()(最实用)
Jsoup 层级关系·速查表
一、向上查找:子 → 父 / 祖先
| 方法 | 作用 | 说明 |
|---|---|---|
parent() | 获取直接父元素 | 只返回上一级 Element |
parentNode() | 获取父节点 | 可能是文本/注释,一般用 parent() 即可 |
closest("css") | 向上找最近匹配的祖先 | 最实用,比如找最近的 div、tr |
parents() | 获取所有祖先元素 | 从父到 html 依次排列 |
二、向下查找:父 → 子 / 后代
| 方法 | 作用 | 范围 |
|---|---|---|
children() | 所有直接子元素 | 只下一级,不含孙子 |
child(index) | 第 index 个直接子元素 | 从 0 开始 |
select("标签") | 所有后代元素 | 子+孙子+所有下层 |
select("> 标签") | 仅直接子元素 | 等价于 CSS 子选择器 |
selectFirst("css") | 后代中第一个匹配元素 | 简化代码,避免空指针 |
三、同级查找:兄弟节点
| 方法 | 作用 |
|---|---|
previousElementSibling() | 上一个兄弟元素 |
nextElementSibling() | 下一个兄弟元素 |
siblingElements() | 所有兄弟元素(不含自己) |
firstElementSibling() | 同级别第一个元素 |
lastElementSibling() | 同级别最后一个元素 |
四、CSS 层级选择器(必背)
| 写法 | 含义 | 示例 |
|---|---|---|
A B | A 下面所有 B(后代) | div p |
A > B | A 直接子 B(仅一层) | div > p |
A + B | A 后面紧跟的 B | h1 + p |
A ~ B | A 后面所有同级 B | h1 ~ p |
五、一句话记忆
- 向下一层:
children()/> - 向下所有层:
select() - 向上一层:
parent() - 向上就近匹配:
closest() - 同级前后:
next/previous