使用lxml标记文本的一部分

问题描述

我正在使用python lxml库处理XML。

我有一段这样的文字,

<p>Lorem ipsum dolor sit amet,consectetur adipiscing elit. Integer facilisis elit eget
condimentum efficitur. Donec eu dignissim lectus. Integer tortor
lacus,porttitor at ipsum quis,tempus dignissim dui. Curabitur cursus
quis arcu in pellentesque. Aenean volutpat,tortor a commodo interdum,lorem est convallis dui,sodales imperdiet ligula ligula non felis.</p>

假设我想用上面的标签标记上一段中的特定文本,例如“ tor lacus,ipsum quis的porttitor,tempus ”。我将如何使用lxml做到这一点。现在我正在使用文本替换,但是我觉得这不是正确的方法。

即我要找的结果是

<p>Lorem ipsum dolor sit amet,consectetur adipiscing elit. Integer facilisis elit eget
condimentum efficitur. Donec eu dignissim lectus. Integer <foobar>tortor
lacus,tempus</foobar> dignissim dui. Curabitur cursus 
quis arcu in pellentesque. Aenean volutpat,sodales imperdiet ligula ligula non felis.</p>

解决方法

在lxml中用实际元素替换文本是棘手的;尤其是如果您有混合的内容(文本和子元素的混合)。

棘手的部分是知道如何处理其余文本以及在何处插入元素。其余文本是否应该是父.text的一部分?它应该是前一个兄弟的.tail的一部分吗?应该是新元素的.tail的一部分吗?

我过去所做的是处理所有text()节点,并在文本中添加占位符字符串(无论是.text还是.tail)。然后,我将树序列化为字符串,然后搜索并替换占位符。之后,我要么将字符串解析为XML以构建新树(以进行进一步处理,验证,分析等),要么将其写入文件。

在这种情况下,请参阅我的related question / answer以获取有关.text / .tail的其他信息。

这是一个基于我在上述问题中的回答的示例。

注意:

  • 我添加了gotcha元素以显示其如何处理混合内容。
  • 我添加了第二个搜索字符串(Aenean volutpat),以显示替换了多个字符串。
  • 在此示例中,我仅处理p子级的text()节点。

Python

import re
from lxml import etree

xml = """<doc>
<p>Lorem ipsum dolor <gotcha>sit amet</gotcha>,consectetur adipiscing elit. Integer facilisis elit eget
condimentum efficitur. Donec eu dignissim lectus. Integer tortor
lacus,porttitor at ipsum quis,tempus dignissim dui. Curabitur cursus
quis arcu <gotcha>in pellentesque</gotcha>. Aenean volutpat,tortor a commodo interdum,lorem est convallis dui,sodales imperdiet ligula ligula non felis.</p>
</doc>
"""


def update_text(orig_text,phrase_list,elemname):
    new_text = orig_text
    for phrase in phrase_list:
        if phrase in new_text:
            # Add placeholders for the new start/end tags.
            new_text = new_text.replace(phrase,f"[elemstart:{elemname}]{phrase}[elemend:{elemname}]")
        else:
            new_text = new_text
    return new_text


root = etree.fromstring(xml)

foobar_phrases = {"tortor lacus,tempus","Aenean volutpat"}

for text in root.xpath("//p/text()"):
    parent = text.getparent()
    updated_text = update_text(text.replace("\n"," "),foobar_phrases,"foobar")
    if text.is_text:
        parent.text = updated_text
    elif text.is_tail:
        parent.tail = updated_text

# Serialze the tree to a string so we can replace the placeholders with proper tags.
serialized_tree = etree.tostring(root,encoding="utf-8").decode()
serialized_tree = re.sub(r"\[elemstart:([^\]]+)\]",r"<\1>",serialized_tree)
serialized_tree = re.sub(r"\[elemend:([^\]]+)\]",r"</\1>",serialized_tree)

# Now we can either parse the string back into a tree (for additional processing,validation,etc.),# print it,write it to a file,etc.
print(serialized_tree)

打印输出(添加了换行符以提高可读性)

<doc>
<p>Lorem ipsum dolor <gotcha>sit amet</gotcha>,consectetur adipiscing elit. 
Integer facilisis elit eget condimentum efficitur. Donec eu dignissim lectus.
Integer <foobar>tortor lacus,tempus</foobar> dignissim dui.
Curabitur cursus quis arcu <gotcha>in pellentesque</gotcha>. <foobar>Aenean volutpat</foobar>,sodales imperdiet ligula ligula non felis.</p>
</doc>
,

您可以像这样检查是否有孩子:

from lxml import etree

root = etree.parse("test.xml").getroot()
paragraphs = root.findall("p")

print(f"Found {len(paragraphs)} paragraphs")

for i in range(len(paragraphs)):
    if len(list(paragraphs[i])) > 0:
        print(f"Paragraph {i} has children")
    else:
        print(f"Paragraph {i} has no children")

首先,代码过滤所有段落,然后查看该段落是否包含子项。

现在,如果您没有孩子,则可以像以前一样替换文本;如果您有孩子,则可以替换整个孩子

,

如果<p>标签不会嵌套在另一个<p>中,则可以考虑使用正则表达式替换

import re

a="""
other lines here that may contain foo
<p>
this is a foo inside para
and this is new line in this foo para
</p>
excess lines here that also may contain foo in it.
"""

search="foo"
newtagname="bar"

b=re.sub("("+search+")(?=[^><]*?</p>)","<"+newtagname+">\\1</"+newtagname+">",a)

print(b)

此打印

other lines here that may contain foo
<p>
this is a <bar>foo</bar> inside para
and this is new line in this <bar>foo</bar> para
</p>
excess lines here that also may contain foo in it.

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...