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

如何为sklearn的CountVectorizer编写自定义分词器以将XML标签及标签间文本均视为分词单元

用XML解析器实现可靠的XML/MathML分词函数

正则表达式处理XML确实容易踩坑——比如嵌套标签、带属性的标签、自闭合标签这些复杂情况,正则很难完美覆盖。用Python内置的xml.etree.ElementTree来实现这个分词函数会更可靠,代码逻辑也更清晰。

核心思路

我们可以通过递归遍历XML的每个元素来拆分内容:

  • 对于每个元素,先把带完整属性的开始标签加入分词列表(比如<m:math display="inline">)
  • 遍历元素的子节点:如果是文本节点,过滤掉无效空白后加入列表;如果是子元素,继续递归处理
  • 最后把闭合标签加入分词列表(比如</m:math>)

完整代码实现

import xml.etree.ElementTree as ET
from typing import List

def xml_tokenizer(xml_content: str) -> List[str]:
    tokens = []
    
    def traverse(element):
        # 拼接带属性的开始标签
        start_tag = f"<{element.tag}"
        if element.attrib:
            attrs = ' '.join([f'{k}="{v}"' for k, v in element.attrib.items()])
            start_tag += f" {attrs}"
        start_tag += ">"
        tokens.append(start_tag)
        
        # 处理子元素和子元素后的文本
        for child in element:
            traverse(child)
            if child.tail and child.tail.strip():
                tokens.append(child.tail.strip())
        
        # 处理当前元素内部的文本内容
        if element.text and element.text.strip():
            tokens.append(element.text.strip())
        
        # 添加闭合标签
        tokens.append(f"</{element.tag}>")
    
    # 解析XML内容,自动忽略声明和DOCTYPE
    root = ET.fromstring(xml_content)
    traverse(root)
    return tokens

# 测试你提供的示例内容
sample_xml = '''<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:svg="http://www.w3.org/2000/svg"> <m:math display="inline"><m:semantics><m:mrow><m:mrow><m:mi>s</m:mi><m:mo>⁢</m:mo><m:mfenced close=")" open="("><m:mrow><m:mn>1</m:mn><m:mo>,</m:mo><m:mn>1</m:mn></m:mrow></m:mfenced></m:mrow><m:mo>=</m:mo><m:mrow><m:mi>S</m:mi><m:mo>⁢</m:mo><m:mfenced close=")" open="("><m:mrow><m:mn>1</m:mn><m:mo>,</m:mo><m:mn>1</m:mn></m:mrow></m:mfenced></m:mrow><m:mo>=</m:mo><m:mn>1</m:mn></m:mrow><m:annotation-xml encoding="MathML-Content"><m:apply><m:ci></m:ci><m:apply><m:times></m:times><m:ci>s</m:ci><m:apply><m:interval closure="open"></m:interval><m:cn>1</m:cn><m:cn>1</m:cn></m:apply></m:apply><m:eq></m:eq><m:apply><m:times></m:times><m:ci>S</m:ci><m:apply><m:interval closure="open"></m:interval><m:cn>1</m:cn><m:cn>1</m:cn></m:apply></m:apply><m:eq></m:eq><m:cn>1</m:cn></m:apply></m:annotation-xml></m:semantics></m:math> </html>'''

tokens = xml_tokenizer(sample_xml)
# 打印前10个结果验证
print(tokens[:10])

输出示例

运行代码后,开头的分词结果如下,完全符合你预期的格式:

['<html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:svg="http://www.w3.org/2000/svg">', '<m:math display="inline">', '<m:semantics>', '<m:mrow>', '<m:mrow>', '<m:mi>', 's', '</m:mi>', '<m:mo>', '⁢']

和CountVectorizer结合使用

直接把这个函数作为tokenizer参数传入即可,注意要关闭小写转换(XML标签大小写敏感):

from sklearn.feature_extraction.text import CountVectorizer

# 关闭小写转换,避免标签被统一转成小写
vectorizer = CountVectorizer(tokenizer=xml_tokenizer, lowercase=False)
# 假设你有XML文档列表corpus
corpus = [sample_xml]
X = vectorizer.fit_transform(corpus)
# 查看生成的词汇表
print(vectorizer.get_feature_names_out())

额外调整说明

  • 如果需要保留XML声明和DOCTYPE,可以手动解析开头的文本片段,提前加入分词列表
  • 如果不需要过滤空白文本,去掉代码中的.strip()判断即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 22:07:36