如何使用Jsoup解析嵌套描述列表(dl标签)?
用Jsoup解析任意深度的嵌套描述列表
嘿,这个问题我熟!处理嵌套的<dl>确实不能只用扁平化循环,递归才是解决任意深度嵌套的关键。我来给你捋捋怎么做~
首先,先看你提供的HTML片段(我稍微补全了缺失的标签,方便演示):
<dt><span class="chapter"><a href="jls-1.html">1. Introduction</a></span></dt> <dd> <dl> <dt><span class="section"><a href="jls-1.html#jls-1.1">1.1. Organization of the Specification</a></span></dt> <dt><span class="section"><a href="jls-1.html#jls-1.2">1.2. Scope</a></span></dt> <dd> <dl> <dt><span class="subsection"><a href="jls-1.html#jls-1.2.1">1.2.1. What's Included</a></span></dt> </dl> </dd> </dl> </dd>
核心思路:递归遍历嵌套结构
因为<dl>可以无限嵌套在<dd>里,扁平化的for循环只能处理第一层,遇到子<dl>就会失效。递归的方式可以逐层深入,不管嵌套多少层都能覆盖到。
步骤1:解析HTML并定位根元素
首先用Jsoup解析你的HTML,找到最外层的<dl>(如果没有明确的根<dl>,直接从文档根节点开始也可以)。
String html = "你的HTML内容"; Document doc = Jsoup.parse(html); // 定位根dl,如果是嵌套在某个容器里,就调整选择器 Element rootDl = doc.selectFirst("dl");
步骤2:写递归方法处理每个层级
我们可以写一个递归方法,负责遍历当前层级的<dt>,并处理对应的<dd>里的子<dl>。如果需要把结果存成树形结构,可以先定义一个简单的实体类:
// 用来存储每个章节/小节的信息 static class Section { String title; String url; List<Section> subSections = new ArrayList<>(); }
然后实现递归解析方法:
private static List<Section> parseNestedDl(Element parent) { List<Section> sections = new ArrayList<>(); // 获取当前父元素下所有的<dt> Elements dts = parent.select("dt"); for (Element dt : dts) { Section section = new Section(); // 提取标题文本 section.title = dt.text(); // 提取链接(如果存在的话) Element link = dt.selectFirst("a"); section.url = link != null ? link.attr("href") : ""; // 找到当前<dt>对应的<dd>(dt和dd是兄弟节点,逐个找下一个兄弟直到遇到dt或null) Element dd = dt.nextElementSibling(); while (dd != null && !dd.tagName().equals("dt")) { // 检查<dd>里有没有子<dl> Elements subDls = dd.select("dl"); if (!subDls.isEmpty()) { // 递归解析子<dl>,把结果添加到当前section的子列表中 for (Element subDl : subDls) { section.subSections.addAll(parseNestedDl(subDl)); } } // 继续找下一个兄弟元素,避免遗漏多个<dd>的情况 dd = dd.nextElementSibling(); } sections.add(section); } return sections; }
步骤3:调用方法并使用结果
现在你可以调用这个方法,得到一个树形的章节列表,之后可以遍历它输出或者做其他处理:
List<Section> allSections = parseNestedDl(rootDl); // 示例:递归打印所有章节 printSections(allSections, 0);
辅助打印方法:
private static void printSections(List<Section> sections, int indentLevel) { String indent = " ".repeat(indentLevel); for (Section section : sections) { System.out.printf("%s%s (%s)%n", indent, section.title, section.url); // 递归打印子章节 printSections(section.subSections, indentLevel + 1); } }
关键细节说明
- dt和dd的对应关系:注意
<dt>和<dd>是兄弟节点,一个<dt>可能对应多个<dd>,所以要用nextElementSibling()循环查找,直到遇到下一个<dt>或者null。 - 递归终止条件:当当前元素下没有
<dt>或者子<dl>时,递归自然终止。 - 灵活性:这个方法可以适配任意深度的嵌套,不管是章节、小节、子小节都能处理。
内容的提问来源于stack exchange,提问作者my-lord
相关产品推荐
相关产品推荐

