python lxml 使用 iterparse 编辑并输出 xml
我最近在玩lxml这个库,可能我理解得不太对或者漏掉了什么,但我就是搞不清楚怎么在抓到某个xpath后编辑文件,然后把它写回到xml里,同时一个一个元素地解析。
比如说,我们有这样一个xml作为例子:
<xml>
<items>
<pie>cherry</pie>
<pie>apple</pie>
<pie>chocolate</pie>
</items>
</xml>
我想在解析的时候,当我找到"/xml/items/pie"这个xpath时,想在pie前面加一个元素,这样就变成这样:
<xml>
<items>
<item id="1"><pie>cherry</pie></item>
<item id="2"><pie>apple</pie></item>
<item id="3"><pie>chocolate</pie></item>
</items>
</xml>
这个输出需要在我每次遇到标签并在特定的xpath处编辑xml时,一行一行地写入文件。我是可以通过硬编码某些部分来打印起始标签、文本、属性(如果有的话)和结束标签,但这样会显得很乱,如果能有办法避免那样就好了。
这是我猜的代码:
from lxml import etree
path=[]
count=0
context=etree.iterparse(file,events=('start','end'))
for event, element in context:
if event=='start':
path.append(element.tag)
if /'+'/'.join(path)=='/xml/items/pie':
itemnode=etree.Element('item',id=str(count))
itemnode.text=""
element.addprevious(itemnode)#Not the right way to do it of course
#write/print out xml here.
else:
element.clear()
path.pop()
补充:另外,我需要处理比较大的文件,所以我必须使用iterparse。
3 个回答
0
我建议你使用一个XSLT模板,因为这似乎更适合这个任务。一开始,XSLT可能有点难懂,直到你习惯它为止。如果你只是想从XML生成一些输出,那么XSLT是个很不错的工具。
1
有一种更简单的方法来进行你需要的修改:
- 遍历
pie
元素 - 创建一个
item
元素 - 使用 replace() 方法把
pie
元素替换成item
replace(self, old_element, new_element)
用第二个参数传入的元素替换掉一个子元素。
from lxml import etree
from lxml.etree import XMLParser, Element
data = """<xml>
<items>
<pie>cherry</pie>
<pie>apple</pie>
<pie>chocolate</pie>
</items>
</xml>"""
tree = etree.fromstring(data, parser=XMLParser())
items = tree.find('.//items')
for index, pie in enumerate(items.xpath('.//pie'), start=1):
item = Element('item', {'id': str(index)})
items.replace(pie, item)
item.append(pie)
print etree.tostring(tree, pretty_print=True)
输出:
<xml>
<items>
<item id="1"><pie>cherry</pie></item>
<item id="2"><pie>apple</pie></item>
<item id="3"><pie>chocolate</pie></item>
</items>
</xml>
2
这里有一个使用 iterparse()
的解决方案。这个方法的思路是捕捉所有标签的“开始”事件,记住父标签(items
),然后对于每一个 pie
标签,创建一个 item
标签,并把这个派放进去:
from StringIO import StringIO
from lxml import etree
from lxml.etree import Element
data = """<xml>
<items>
<pie>cherry</pie>
<pie>apple</pie>
<pie>chocolate</pie>
</items>
</xml>"""
stream = StringIO(data)
context = etree.iterparse(stream, events=("start", ))
for action, elem in context:
if elem.tag == 'items':
items = elem
index = 1
elif elem.tag == 'pie':
item = Element('item', {'id': str(index)})
items.replace(elem, item)
item.append(elem)
index += 1
print etree.tostring(context.root)
输出结果是:
<xml>
<items>
<item id="1"><pie>cherry</pie></item>
<item id="2"><pie>apple</pie></item>
<item id="3"><pie>chocolate</pie></item>
</items>
</xml>