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

如何使用Jericho HTML Parser递归解析HTML元素?求实现指引

递归解析HTML并转换标签的实现思路(基于Jericho HTML Parser)

我需要用Jericho HTML Parser递归解析一段HTML,核心需求是:提取元素的wicket:id属性值作为新标签名;没有该属性的标准HTML标签保持原样。目前的代码无法实现递归逻辑,求可行的实现思路。

HTML内容

<div wicket:id="Container1">
        <div wicket:id="Panel1"></div>
        <form wicket:id="sampleForm" action="">
            <div id="InterstitialPanel" class="usaa-interstitial s1"></div>
            <div wicket:id="DisclosureSection">
                <div class="sample class">
                    <ul>
                        <li>Item 1</li>
                        <li>Item 2</li>
                        <li>Item 3</li>
                        <li>Item 4</li>
                    </ul>
                </div>
            </div>
        </form> 
    </div>

    <div wicket:id="Container2">
        <h2>This is container 2</h2>
    </div>

预期输出

需求明确:从原始HTML中提取wicket:id属性值创建新标签;无wicket属性的标准HTML标签保持原样。

<Container1> 
<Panel1></Panel1>
<Form id="sampleForm">
    <InterstitialPanel></InterstitialPanel>
    <DisclosureSection>
        <div class="sample class">
                    <ul>
                        <li>Item 1</li>
                        <li>Item 2</li>
                        <li>Item 3</li>
                        <li>Item 4</li>
                    </ul>
        </div>
    </DisclosureSection>
</Form>

    This is container 2

现有Java代码

public static void main(String args[]) {
    Main m = new Main();
    File file = new File("C:\\USAAPoC\\ClientSamplePage.html");
    Source doc = new Source(file);
    Element element = doc.getElementByTag("body");
    m.processBody(element, new ReactPageContext());
}

private StringBuilder processBody(Element element, ReactPageContext reactPageContext) {
    StringBuilder sb = new StringBuilder();
    if (!element.getChildElements().isEmpty()) {
        element.getChildElements().stream().forEach(child -> sb.append(processTag(child, reactPageContext)));
    } else {
        sb.append("<Container>\n");
    }

    return sb;
}

private StringBuilder processTag(Element element, ReactPageContext reactPageContext) {
    StringBuilder sb = new StringBuilder();

    element.getAllElements().stream().forEach(child -> {
            if (child.getName().equals("div") && child.getAttributes().get("wicket:id") != null) {
                sb.append("<Container>");
            } else if (child.getName().equals("form")) {
                    
                    sb.append("<Form>");
                    List<Element> allChildFormElements = outputDocument.getSegment().getAllElements();
                    allChildFormElements.stream().forEach(childFormElement -> {
                        if (childFormElement.getName().equals("div")
                                && childFormElement.getAttributes().get("wicket:id") != null) {
                                sb.append("<Container>");
                        } else {
                            // 未实现的逻辑
                        }
                    });
            }
    });

    return sb;
}

实现思路与重构代码

核心递归逻辑

递归的核心是每个元素处理自身后,必须递归处理其所有子节点,不管是带wicket:id的自定义标签,还是普通HTML标签、文本节点,都要逐层遍历处理。

具体处理规则

  1. 标签转换规则:
    • 若元素存在wicket:id属性:
      • 除<form>外,直接用wicket:id的值作为新标签名,丢弃原标签和属性
      • <form>需特殊处理:新标签名为<Form>,并将wicket:id的值转为id属性
    • 若无wicket:id属性:保留原标签名和所有属性
  2. 文本节点处理:直接保留文本内容,不要忽略
  3. 闭合标签:所有标签必须正确闭合,空标签也要生成完整的闭合结构(如<Panel1></Panel1>)

重构后的代码示例

import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.Source;
import net.htmlparser.jericho.StartTag;

import java.io.File;
import java.util.List;

public class HtmlParser {

    public static void main(String args[]) throws Exception {
        HtmlParser parser = new HtmlParser();
        File file = new File("C:\\USAAPoC\\ClientSamplePage.html");
        Source doc = new Source(file);
        Element bodyElement = doc.getElementByTag("body");
        StringBuilder result = parser.processElement(bodyElement);
        System.out.println(result.toString());
    }

    private StringBuilder processElement(Element element) {
        StringBuilder sb = new StringBuilder();

        // 处理当前元素的开始标签
        StartTag startTag = element.getStartTag();
        String wicketId = startTag.getAttributeValue("wicket:id");

        if (wicketId != null) {
            if (element.getName().equalsIgnoreCase("form")) {
                // Form标签特殊处理:保留id属性
                sb.append(String.format("<Form id=\"%s\">\n", wicketId));
            } else {
                // 其他带wicket:id的元素,直接用属性值作为标签名
                sb.append(String.format("<%s>\n", wicketId));
            }
        } else {
            // 无wicket:id的元素,保留原标签和属性
            sb.append(startTag.toString());
            sb.append("\n");
        }

        // 递归处理子节点:包括元素节点和文本节点
        List<?> childNodes = element.getChildNodes();
        for (Object node : childNodes) {
            if (node instanceof Element) {
                sb.append(processElement((Element) node));
            } else {
                // 处理文本节点,去除多余空白(可选,根据需求调整)
                String text = node.toString().trim();
                if (!text.isEmpty()) {
                    sb.append(text).append("\n");
                }
            }
        }

        // 处理闭合标签
        if (wicketId != null) {
            if (element.getName().equalsIgnoreCase("form")) {
                sb.append("</Form>\n");
            } else {
                sb.append(String.format("</%s>\n", wicketId));
            }
        } else {
            sb.append(element.getEndTag().toString()).append("\n");
        }

        return sb;
    }
}

关键改进点

  1. 合并processBody和processTag为统一的processElement方法,递归处理所有元素
  2. 覆盖所有节点类型:不仅处理元素节点,还处理文本节点
  3. 明确标签转换规则,包括form的特殊处理
  4. 自动处理标签闭合,避免遗漏
  5. 修复原代码中outputDocument未定义的错误

内容的提问来源于stack exchange,提问作者Sai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:45:46