删除跨越XML树分支的两个标签之间的所有内容
我正在尝试使用Python和lxml删除XML文档中两个标签之间的所有内容。问题是这些标签可能在树的不同分支上(但总是在相同的深度)。下面是一个示例文档。
<root>
<p> Hello world <start />this is a paragraph </p>
<p> Goodbye world. <end />I'm leaving now </p>
</root>
我想删除开始标签和结束标签之间的所有内容,这样最终只会剩下一个p标签:
<root>
<p> Hello world I'm leaving now </p>
</root>
有没有人知道如何使用lxml和Python来实现这个?
3 个回答
我知道可能会有人因为这个想法想要攻击我,但你可以直接用正则表达式来解决这个问题:
import re
new_string = re.sub(r'<start />(.*?)<end />', '', your_string, re.S)
当你的内容不是有效的XML时,就不能使用XML解析器。
你现在遇到了一团糟,真应该给那个故意搞乱XML嵌套规则的人一个教训。
你最好使用像SAX这样的工具,来识别<start/>
标签,然后开始丢弃输入,直到遇到<end/>
。SAX的好处在于,它可以让你在处理每一个词法单元时采取任意的操作,而lxml在你接触到它们之前就已经把开始和结束标签分开了。
顺便说一下,你可能还想把这些文档转换成可用的XML格式。
你可以试试使用类似SAX的目标解析器接口:
from lxml import etree
class SkipStartEndTarget:
def __init__(self, *args, **kwargs):
self.builder = etree.TreeBuilder()
self.skip = False
def start(self, tag, attrib, nsmap=None):
if tag == 'start':
self.skip = True
if not self.skip:
self.builder.start(tag, attrib, nsmap)
def data(self, data):
if not self.skip:
self.builder.data(data)
def comment(self, comment):
if not self.skip:
self.builder.comment(self)
def pi(self, target, data):
if not self.skip:
self.builder.pi(target, data)
def end(self, tag):
if not self.skip:
self.builder.end(tag)
if tag == 'end':
self.skip = False
def close(self):
self.skip = False
return self.builder.close()
然后你可以用SkipStartEndTarget
这个类来创建一个parser target
,并用这个目标创建一个自定义的XMLParser
,像这样:
parser = etree.XMLParser(target=SkipStartEndTarget())
如果你需要的话,你仍然可以给解析器提供其他选项。然后你可以把这个解析器传给你正在使用的解析函数,比如:
elem = etree.fromstring(xml_str, parser=parser)
这个方法同样适用于etree.XML()
和etree.parse()
,你甚至可以用etree.setdefaultparser()
把解析器设置为默认解析器(不过这可能不是个好主意)。有一点需要注意的是:即使使用etree.parse()
,它也不会返回一个元素树,而是总是返回一个元素(就像etree.XML()
和etree.fromstring()
那样)。我觉得目前还做不到这一点,所以如果这对你来说是个问题,你需要想办法解决。
另外,使用lxml.sax从SAX事件创建元素树也是可能的,不过这可能会稍微复杂一些,而且速度也会慢一些。与上面的例子不同,它会返回一个元素树,但我认为它不会提供使用etree.parse()
时能得到的.docinfo
。我还相信它(目前)不支持注释和处理指令(pi)。(我还没用过,所以现在不能更准确地说)
还要注意,任何类似SAX的解析方法都要求在<start/>
和<end/>
之间跳过的内容仍然要形成一个有效的文档。在你的例子中是这样的,但如果第二个<p>
是<p2>
的话就不行了,因为那样你会得到<p>....</p2>
。