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

HTML内容修剪后标签未闭合致结构破损问题及代码修复需求

修复HTML修剪时的结构损坏问题

问题现状

修剪HTML内容后,因标签未正确闭合导致结构损坏,渲染时标签直接可见,示例:

< body> < able> [if mso]> < d> <![endif] [if mso]> < r> < able> <![endif] < d> < r> < body> < able> Viac na Právne noviny < d> < r> < body> < able> < d> < r> < body> < able>

...[trimmed]

原代码仅简单遍历元素并截断文本,未处理未闭合标签,导致HTML结构破坏。

优化后的代码

from bs4 import BeautifulSoup, Tag, NavigableString

def trim_html(html: str) -> str:
    max_length = 70000
    notice = "...[trimmed]"
    notice_length = len(notice)
    
    # 预处理空白字符,判断是否无需修剪
    html = html.replace('\n', '').replace('\t', ' ').replace('\r', '')
    if len(html) <= max_length:
        return html
    
    soup = BeautifulSoup(html, 'html.parser')
    output_soup = BeautifulSoup('', 'html.parser')
    current_parent = output_soup  # 当前输出的父节点指针
    open_tags = []  # 跟踪未闭合的标签栈
    current_length = 0  # 累计已使用的字符长度(含标签和文本)
    
    def traverse(element):
        nonlocal current_length, current_parent
        # 处理标签节点
        if isinstance(element, Tag):
            # 计算起始标签的字符长度
            tag_start = f"<{element.name}"
            if element.attrs:
                for key, value in element.attrs.items():
                    tag_start += f' {key}="{value}"'
            tag_start += ">"
            tag_start_len = len(tag_start)
            
            # 检查剩余空间是否足够放下起始标签
            available = max_length - current_length - notice_length
            if available <= tag_start_len:
                return False
            
            # 创建新标签并挂载到当前父节点
            new_tag = output_soup.new_tag(element.name, attrs=element.attrs)
            current_parent.append(new_tag)
            # 更新长度、标签栈和当前父节点
            current_length += tag_start_len
            open_tags.append(new_tag)
            current_parent = new_tag
        
        # 处理文本节点
        elif isinstance(element, NavigableString):
            available = max_length - current_length - notice_length
            if available <= 0:
                return False
            # 截取可容纳的文本
            text_to_add = element[:available]
            current_parent.append(text_to_add)
            current_length += len(text_to_add)
            # 文本被截断则终止遍历
            if len(text_to_add) < len(element):
                return False
        
        # 递归遍历子元素
        for child in element.children:
            if not traverse(child):
                return False
        
        # 处理标签闭合
        if isinstance(element, Tag):
            tag_end = f"</{element.name}>"
            tag_end_len = len(tag_end)
            # 检查剩余空间是否允许添加闭合标签
            if current_length + tag_end_len + notice_length <= max_length:
                current_length += tag_end_len
                # 回退父节点并弹出标签栈
                open_tags.pop()
                current_parent = open_tags[-1] if open_tags else output_soup
            else:
                # 空间不足则放弃闭合当前标签,直接回退
                open_tags.pop()
                current_parent = open_tags[-1] if open_tags else output_soup
                return False
        return True
    
    # 开始遍历原HTML的根节点子元素
    for child in soup.children:
        if not traverse(child):
            break
    
    # 闭合所有剩余未闭合的标签,确保结构完整
    while open_tags:
        tag = open_tags.pop()
        current_parent = open_tags[-1] if open_tags else output_soup
        tag_end = f"</{tag.name}>"
        # 优先保证结构完整,若剩余空间允许则添加闭合标签
        if current_length + len(tag_end) + notice_length <= max_length:
            current_length += len(tag_end)
            current_parent.append(tag_end)
    
    # 添加修剪提示
    current_parent.append(notice)
    
    return str(output_soup)

核心优化点

  • 采用深度优先遍历+标签栈,跟踪未闭合标签,截断后自动逐层闭合,保证HTML结构完整。
  • 计算总长度时同时包含标签本身的字符长度和文本内容长度,避免总长度超限。
  • 优先保障结构完整性,即使剩余空间仅够闭合标签,也会完成闭合操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:36:17