如何使用Python为带嵌套标签的XML文本添加标记?
解决XML嵌套标签下关键词添加标记的问题
你遇到的问题本质是XML元素的文本并非只存在于text属性中——当有嵌套子元素时,子元素后面的文本会被存在子元素的tail属性里,原代码只处理了父元素的text(也就是第一个子元素之前的文本),自然找不到后面的关键词。
而且直接用字符串拼接<key>标签的方式不可取,这会破坏XML的结构,应该用ElementTree的API来创建和插入元素,保证XML的合法性。
解决思路
- 递归遍历XML的所有元素,处理元素自身的
text和所有子元素的tail - 对于每个文本片段,拆分出关键词,将关键词替换为
<key>元素,把拆分后的文本和新元素重新插入到XML树中
修正后的代码
import xml.etree.ElementTree as ET def wrap_keywords(element, keywords): # 处理当前元素的text内容 if element.text: parts = [] current_pos = 0 text = element.text for keyword in keywords: idx = text.find(keyword, current_pos) while idx != -1: # 添加关键词之前的普通文本 if idx > current_pos: parts.append(text[current_pos:idx]) # 添加关键词对应的<key>元素 key_elem = ET.Element("key") key_elem.text = keyword parts.append(key_elem) current_pos = idx + len(keyword) idx = text.find(keyword, current_pos) # 添加剩余的普通文本 if current_pos < len(text): parts.append(text[current_pos:]) # 清空原text,重新构建子节点 element.text = None for part in parts: if isinstance(part, str): # 文本节点直接作为前一个节点的tail,或元素的text last_child = list(element)[-1] if list(element) else None if last_child is not None: last_child.tail = (last_child.tail or "") + part else: element.text = part else: element.append(part) # 递归处理子元素,并处理子元素的tail for child in list(element): wrap_keywords(child, keywords) # 处理子元素的tail内容 if child.tail: parts = [] current_pos = 0 tail_text = child.tail for keyword in keywords: idx = tail_text.find(keyword, current_pos) while idx != -1: if idx > current_pos: parts.append(tail_text[current_pos:idx]) key_elem = ET.Element("key") key_elem.text = keyword parts.append(key_elem) current_pos = idx + len(keyword) idx = tail_text.find(keyword, current_pos) if current_pos < len(tail_text): parts.append(tail_text[current_pos:]) # 清空原tail,将拆分后的内容插入到当前子元素之后 child.tail = None parent = element child_index = list(parent).index(child) for i, part in enumerate(parts): if isinstance(part, str): # 文本作为前一个节点的tail prev_node = list(parent)[child_index + i] if (child_index + i) < len(list(parent)) else child prev_node.tail = (prev_node.tail or "") + part else: parent.insert(child_index + i + 1, part) # 测试代码 doc = '''<root><par>An <fr>example</fr> text with key words one and two</par></root>''' profs = ['one', 'two'] root = ET.fromstring(doc) wrap_keywords(root, profs) # 输出处理后的XML ET.indent(root) print(ET.tostring(root, encoding='unicode'))
代码说明
wrap_keywords函数递归处理每个元素:- 先处理元素自身的
text,把包含关键词的文本拆分成普通文本和<key>元素,替换原有的text - 递归处理子元素,确保嵌套标签内部的文本也能被处理(如果需要的话)
- 处理子元素的
tail——这就是原代码遗漏的部分,子元素后面的文本都在这里,同样拆分后插入到子元素之后
- 先处理元素自身的
运行后会输出结构合法的XML,其中one和two被正确包裹在<key>标签内:
<root> <par>An <fr>example</fr> text with key words <key>one</key> and <key>two</key></par> </root>
内容的提问来源于stack exchange,提问作者Sergii
相关产品推荐
相关产品推荐

