如何在Python中选择子元素包含一些文本的节点?

时间:2013-02-03 05:04:01

标签: python lxml

我有一个像这样的xml文件:

<data>
      <entry>
           <word>ABC</word> (this)
      </entry>
      <entry>
           <word>ABC</word> [not this]
      </entry>
</data>

我想选择其后代包含“(”,并将(。*)移动到<entry>文本的节点。即:

<data>
      <entry>
           (this)
           <word>ABC</word>
      </entry>
      <entry>
           <word>ABC</word> [not this]
      </entry>
</data>

我正在使用lxml。我试过了:

 import lxml.etree as ET
 data = ET.parse('sample.xml')
 for entry in data.iter('entry'):
      A = entry.xpath('.//*[text() = ".*(.*?)"]')

但它不起作用。 “(”可以显示为节点的尾部或节点的文本。

2 个答案:

答案 0 :(得分:0)

如果(在尾部并将其移动到父文本..那么..

In [67]: myxml="""<data>
    ...:       <entry>
    ...:            <word>ABC</word> (this)
    ...:       </entry>
    ...:       <entry>
    ...:            <word>ABC</word> [not this]
    ...:       </entry>
    ...: </data>"""

In [68]: import StringIO, re, lxml.etree as ET

In [69]: f=StringIO.StringIO(myxml)

In [70]: data=ET.parse(f)

In [71]: print ET.tostring(data)
<data>
      <entry>
           <word>ABC</word> (this)
      </entry>
      <entry>
           <word>ABC</word> [not this]
      </entry>
</data>

In [72]: for elem in data.findall("/entry/"):
    ...:     if re.match(".*\(.*\).*",elem.tail):
    ...:         elem.getparent().text=elem.tail
    ...:         elem.tail=None
    ...:         

In [73]: print ET.tostring(data)
<data>
      <entry> (this)
      <word>ABC</word></entry>
      <entry>
           <word>ABC</word> [not this]
      </entry>
</data>

答案 1 :(得分:0)

这里有一些问题:

首先,您尝试使用xpath进行正则表达式匹配,但是您正在使用=。您的正则表达式也格式不正确。要在xpath中实际进行正则表达式匹配,您需要执行以下操作:

import lxml.etree as ET
data = ET.parse('sample.xml')
regexpNS = "http://exslt.org/regular-expressions"
for entry in data.iter('entry'):
    A = entry.xpath('.//*[re:test(text(), ".*\(.*\).*")]',
                    namespaces={'re':regexpNS})

不幸的是,这实际上对你不起作用,因为你想要尾部的文本,text()中没有。 lxml文档使它看起来应该包含在string()中,但我尝试了它,它也不起作用。我无法使用xpath和lxml找到任何方法。

所以,这是一种使用更多Python和更少xpath的方法:

 import re
 import lxml.etree as ET
 rx = re.compile('.*\(.*\).*')
 data = ET.parse('sample.xml')
 for entry in data.iter('entry'):
    for child in entry.xpath('.//*'):
        if rx.match(child.text + child.tail):
            # Your manipulations go here
            print child

在任何一种情况下,一个令人愉快的副作用是这个正则表达式在雪中完全摇滚的好时光:.*\(.*\).*