You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:06:06