13

lxml の ElementTree API の実装を使用して XML ドキュメントから特定の要素を完全に削除するのは簡単ですが、要素を何らかのテキストで一貫して置き換える簡単な方法はわかりません。たとえば、次の入力があるとします。

input = '''<everything>
<m>Some text before <r/></m>
<m><r/> and some text after.</m>
<m><r/></m>
<m>Text before <r/> and after</m>
<m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
</everything>
'''

<r>...次の方法ですべての要素を簡単に削除できます。

from lxml import etree
f = etree.fromstring(data)
for r in f.xpath('//r'):
    r.getparent().remove(r)
print etree.tostring(f, pretty_print=True)

ただし、各要素をテキストに置き換えて、出力を取得するにはどうすればよいでしょうか。

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/>Text after a sibling DELETED Text before a sibling<b/></m>
</everything>

ElementTree API は、ツリー内のノードではなく、各要素の属性.textと属性を介してテキストを処理するため、要素に兄弟要素があるかどうかに応じて、多くの異なるケースに対処する必要があることを意味します。.tail既存の要素には.tail属性がありました。これを行う簡単な方法を見逃しましたか?

4

3 に答える 3

20

unutbu の XSLT ソリューションは、おそらく目標を達成するための正しい方法だと思います。

ただし、<r/>タグの末尾を変更してから を使用するという、ややハックな方法がありますetree.strip_elements

from lxml import etree

data = '''<everything>
<m>Some text before <r/></m>
<m><r/> and some text after.</m>
<m><r/></m>
<m>Text before <r/> and after</m>
<m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
</everything>
'''

f = etree.fromstring(data)
for r in f.xpath('//r'):
  r.tail = 'DELETED' + r.tail if r.tail else 'DELETED'

etree.strip_elements(f,'r',with_tail=False)

print etree.tostring(f,pretty_print=True)

あなたにあげる:

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>
于 2011-03-24T14:13:54.763 に答える
8

を使用すると、他の要素を置き換えながらstrip_elements一部の要素を保持できないという欠点があります。<r>また、インスタンスの存在も必要ですElementTree(そうでない場合もあります)。最後に、XML コメントや処理命令を置き換えるために使用することはできません。以下はあなたの仕事をするべきです:

for r in f.xpath('//r'):
    text = 'DELETED' + r.tail 
    parent = r.getparent()
    if parent is not None:
        previous = r.getprevious()
        if previous is not None:
            previous.tail = (previous.tail or '') + text
        else:
            parent.text = (parent.text or '') + text
        parent.remove(r)
于 2012-05-09T16:50:24.630 に答える
4

ET.XSLT の使用:

import io
import lxml.etree as ET

data = '''<everything>
<m>Some text before <r/></m>
<m><r/> and some text after.</m>
<m><r/></m>
<m>Text before <r/> and after</m>
<m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
</everything>
'''

f=ET.fromstring(data)
xslt='''\
    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    

    <!-- Replace r nodes with DELETED
         http://www.w3schools.com/xsl/el_template.asp -->
    <xsl:template match="r">DELETED</xsl:template>

    <!-- How to copy XML without changes
         http://mrhaki.blogspot.com/2008/07/copy-xml-as-is-with-xslt.html -->    
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|text()|comment()|processing-instruction">
        <xsl:copy-of select="."/>
    </xsl:template>
    </xsl:stylesheet>
'''

xslt_doc=ET.parse(io.BytesIO(xslt))
transform=ET.XSLT(xslt_doc)
f=transform(f)

print(ET.tostring(f))

収量

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>
于 2011-03-24T12:31:48.633 に答える