簡體   English   中英

使用 lxml 標記文本的一部分

[英]Using lxml to tag parts of a text

我正在使用 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>

假設我想用標簽標記到上面段落中存在的特定文本位,例如“ tortor lacus, porttitor at ipsum quis, 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, porttitor at ipsum quis, tempus</foobar> 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>

在 lxml 中用實際元素替換文本很棘手; 特別是如果您有混合內容(文本和子元素的混合)。

棘手的部分是知道如何處理剩余的文本以及在哪里插入元素。 剩余的文本應該是父 .text 的一部分嗎? 它應該是前一個兄弟的 .tail 的一部分嗎? 它應該是新元素的.tail 的一部分嗎?

我過去所做的是處理所有 text() 節點並將占位符字符串添加到文本中(無論是 .text 還是 .tail)。 然后我將樹序列化為一個字符串,並在占位符上進行搜索和替換。 之后,我將字符串解析為 XML 以構建新樹(用於進一步處理、驗證、分析等)或將其寫入文件。

在這種情況下,請參閱我的相關問題/答案以獲取有關 .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, porttitor at ipsum quis, 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, porttitor at ipsum quis, tempus</foobar> dignissim dui.
Curabitur cursus quis arcu <gotcha>in pellentesque</gotcha>. <foobar>Aenean volutpat</foobar>, 
tortor a commodo interdum, lorem est convallis dui, 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.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM