lxml:处理tai时元素addnext()和insert()之间的区别

2024-05-16 21:50:24 发布

您现在位置:Python中文网/ 问答频道 /正文

给定一个lxml元素xml,我通过调用c.getnext()来迭代其所有子元素{}。这是因为如果需要的话,我需要动态地插入子元素,而我不能使用迭代器这样做。所有元素都有texttail集。在

让我用下面的例子来说明addnext()和{}的不同行为。假设一个简单的XML字符串,我将其解析为一个lxml树,然后,出于理智起见,检查它:

>>> import lxml.etree
>>> s = "<p>This is <b>bold</b> and this is italic text.</p>"
# Create a new lxml element.
>>> xml = lxml.etree.fromstring(s)
# Let's look at the element, its child, and all the texts and tails.
>>> lxml.etree.tostring(xml)
b'<p>This is <b>bold</b> and this is italic text.</p>'
>>> xml.text
'This is '
>>> xml.tail
>>> xml[0].text
'bold'
>>> xml[0].tail
' and this is italic text.'

到目前为止还不错,这正是我所期望的(关于lxml表示的更多信息,请参见here)。在

现在我想把单词“italic”包装成标记,就像“bold”被包装成<b>标记一样。为此,我首先找到“italic”子字符串开始的索引:

^{pr2}$

然后创建一个新的lxml元素:

# Create a new element and inspect it.
>>> new_c = lxml.etree.fromstring("<i>italic</i>")
>>> new_c.text
'italic'
>>> new_c.tail
>>>

要将这个新元素正确地插入到xml树中,我必须将原始的xml[0].tail字符串拆分为两个子字符串,并从中删除“italic”:

>>> new_c.tail = xml[0].tail[idx+len("italic"):]
>>> xml[0].tail = xml[0].tail[:idx]

现在一切都准备好了,把新元素插入到xml元素中,这就是我现在困惑的地方。在给定的子元素new_c之后插入新的子元素xml[0],结果不同,Element API没有给我任何新的信息:

# Adds the element as a following sibling directly after this element.
# Note that tail text is automatically discarded when adding at the root level.
>>> xml[0].addnext(new_c)
>>> lxml.etree.tostring(xml)
b'<p>This is <b>bold</b><i>italic</i> text. and this is </p>'

以及

# Inserts a subelement at the given position in this element
>>> xml.insert(1 + xml.index(xml[0]), new_c)
>>> lxml.etree.tostring(xml)
b'<p>This is <b>bold</b> and this is <i>italic</i> text.</p>'

这两个调用似乎处理tail的方式不同(请参阅关于tailaddnext()的注释)。即使将注释考虑在内,文本也不会从<b>中丢弃,而是附加到<i>之后,根级别的处理方式也不会与下一级有任何不同(即,可以通过将s中的原始XML包装到另一个<foo>标记中观察到完全相同的行为)。在

我错过了什么?在

编辑关于lxml邮件列表的相关讨论是here。在


Tags: andthetext元素newisxmlelement
2条回答

tail只存在于lxml的级别上;在libxml2中,它是一个文本节点,就像它在DOM中一样。主要原因是解析格式良好的XML(http://lxml.de/tutorial.html#elements-contain-text)时的便利性:

The two properties .text and .tail are enough to represent any text content in an XML document. This way, the ElementTree API does not require any special text nodes in addition to the Element class, that tend to get in the way fairly often (as you might know from classic DOM APIs).

{afs>努力从源代码维护所有抽象的函数。E、 g.index()只统计元素/注释/entityrefs/PI节点,而树操作例程似乎总是会移动节点的尾部。但是,由于这个概念

  • 是如此的缺乏记录
  • 是为用户不关心尾随文本的XML而定制的
  • 与常规代表权冲突

它的应用似乎有不一致之处。这看起来像是一个错误(如果一致性是一个目标的话也是一个bug)。我将与维护人员讨论最后一条语句,以澄清库关于tails的预期行为。在

elem.addnext(nextelem)在XML级别进行操作,即直接在元素之后添加内容,将任何尾部文本移到新插入的元素后面。这样做是为了使新元素成为一个直接跟在后面的同级元素。在

parent.insert(where,elem)的工作方式与父元素只是etree.Element的列表一样。它将新元素放入列表中,而不会对etree.元素实例。parent.append(elem)也可以这样工作,或者任何其他的列表操作。在

因此,这些函数在元素树上有两个不同的视图。在

>>> from lxml import etree as et
>>> 
>>> x = et.XML('<a>foo<b/>bar</a>')
>>> y = et.XML('<c>C!</c>')
>>> 
>>> et.dump(x)
<a>foo<b/>bar</a>
>>> x.find('b').addnext(y)
>>> et.dump(x)
<a>foo<b/><c>C!</c>bar</a>

尾部从b元素移动到c元素,以保持除了插入元素之外的XML文档不变。在

现在,如果插入的元素已经有尾部,addnext用于插入元素及其后面的文本。直接在XML元素之后,而不是在带有tail的etree元素之后。在

^{pr2}$

相关问题 更多 >