我有一个非常大的.xml文件,我正在尝试制作一个新的.xml文件,该文件只包含了该较大文件内容的一小部分。我想指定一个属性(在我的情况下为itemID),并为其指定一些特定的值,然后它将除去所有具有那些itemID及其子元素的元素。
我的大型.xml文件如下所示:
<?xml version='1.0' encoding='UTF-8'?> <api version="2"> <currentTime>2013-02-27 17:00:18</currentTime> <result> <rowset name="assets" key="itemID" columns="itemID,locationID,typeID,quantity,flag,singleton"> <row itemID="1008551770576" locationID="31000559" typeID="17187" quantity="1" flag="0" singleton="1" rawQuantity="-1" /> <row itemID="1008700753886" locationID="31000559" typeID="17187" quantity="1" flag="0" singleton="1" rawQuantity="-1" /> <row itemID="1008700756994" locationID="31000559" typeID="17184" quantity="1" flag="0" singleton="1" rawQuantity="-1" /> <row itemID="1008701224901" locationID="31000559" typeID="17186" quantity="1" flag="0" singleton="1" rawQuantity="-1" /> <row itemID="1004072840841" locationID="31002238" typeID="17621" quantity="1" flag="0" singleton="1" rawQuantity="-1"> <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton"> <row itemID="150571923" typeID="25863" quantity="2" flag="119" singleton="0" /> <row itemID="188435728" typeID="3388" quantity="1" flag="119" singleton="0" /> <row itemID="210122947" typeID="3419" quantity="4" flag="119" singleton="0" /> </rowset> </row> <row itemID="1005279202146" locationID="31002238" typeID="17621" quantity="1" flag="0" singleton="1" rawQuantity="-1"> <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton"> <row itemID="1004239962001" typeID="16275" quantity="49596" flag="4" singleton="0" /> <row itemID="1005364142068" typeID="4246" quantity="156929" flag="4" singleton="0" /> <row itemID="1005624252854" typeID="4247" quantity="93313" flag="4" singleton="0" /> </rowset> </row> <row itemID="1004388226389" typeID="648" quantity="1" flag="0" singleton="1" rawQuantity="-1"> <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton"> <row itemID="1004388228218" typeID="31119" quantity="1" flag="92" singleton="1" rawQuantity="-1" /> <row itemID="1004388701243" typeID="31119" quantity="1" flag="94" singleton="1" rawQuantity="-1" /> <row itemID="1004388701485" typeID="31119" quantity="1" flag="93" singleton="1" rawQuantity="-1" /> <row itemID="1009147502645" typeID="51" quantity="1" flag="5" singleton="1" rawQuantity="-1" /> </rowset> </row> </rowset> </result> <cachedUntil>2013-02-27 23:00:18</cachedUntil> </api>
该文件大约有9万行,大约9兆字节。
注意如何有itemID,某些项目类型可以(但不总是)在其中包含更多项目,并且这些子项也有自己的itemID。我正在尝试获取一些特定的itemID和他们的孩子,而忽略其他所有ID。
我使用了此答案中的代码,它使我非常接近。除了将我使用的itemID的子项排除在外之外,它是完美的。
我的代码如下所示:
import lxml.etree as le ##Take this big .xml file and pull out just the parts we want then write those to a new .xml file## with open(filename,'r') as f: doc=le.parse(f) for elem in doc.xpath('//*[attribute::itemID]'): if elem.attrib['itemID']=='1004072840841': elem.attrib.pop('itemID') else: parent=elem.getparent() parent.remove(elem) print(le.tostring(doc))
这是结果打印出来的样子:
<api version="2"> <currentTime>2013-03-01 21:46:52</currentTime> <result> <rowset name="assets" key="itemID" columns="itemID,locationID,typeID,quantity,flag,singleton"> <row locationID="31002238" typeID="17621" quantity="1" flag="0" singleton="1" rawQuantity="-1"> <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton"> </rowset> </row> </rowset> </result> <cachedUntil>2013-03-02 03:46:53</cachedUntil> </api>
我希望它看起来像这样:
<api version="2"> <currentTime>2013-03-01 21:46:52</currentTime> <result> <rowset name="assets" key="itemID" columns="itemID,locationID,typeID,quantity,flag,singleton"> <row locationID="31002238" typeID="17621" quantity="1" flag="0" singleton="1" rawQuantity="-1"> <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton"> <row itemID="150571923" typeID="25863" quantity="2" flag="119" singleton="0" /> <row itemID="188435728" typeID="3388" quantity="1" flag="119" singleton="0" /> <row itemID="210122947" typeID="3419" quantity="4" flag="119" singleton="0" /> </rowset> </row> </rowset> </result> <cachedUntil>2013-03-02 03:46:53</cachedUntil> </api>
我对代码的理解不够充分,看不到需要更改什么才能包含我要搜索的itemID的子代。另外,理想情况下,我将能够放入多个itemID,它将除去那些itemID及其子对象之外的所有对象。这意味着它将需要保留itemID=[number]row属性(以便在使用此xml文件时可以使用xPath引用特定的itemID及其子级。)
itemID=[number]
所以我的主要问题是关于如何将搜索到的itemID的子项包含在生成的.xml中。我的第二个问题是关于如何同时对一个以上的itemID执行此操作(这样,生成的.xml文件将删除除那些itemID及其子对象之外的所有对象)。
我发现那elem.attrib.pop('itemID')部分是取出itemID的部分,由于我想拥有多个itemID,而他们的孩子仍然留着,所以我需要保留它,所以我取出了那部分。我试图找到一种方法来跳过带有正在搜索的itemID的行的子项,而我想到的是用一个属性标记每个人,然后我可以搜索并删除所有不存在的属性具有该属性。我不需要我做的flag属性,因此我继续将其用于此目的(因为当我尝试遍历它们时,尝试引入新属性遇到了关键错误。)孩子还不够,我还得给孩子的孩子打标签。
elem.attrib.pop('itemID')
这是我的丑陋解决方案:
with open(filename,'r') as f: doc=le.parse(f) for elem in doc.xpath('//*[attribute::itemID]'): if elem.attrib['itemID']=='1004072840841' or elem.attrib['itemID']=='1005279202146': # this or statement lets me get a resulting .xml file that has two itemIDs and their children elem.attrib['flag']='Keep' for child in elem.iterchildren(): child.attrib['flag']='Keep' for c in child.iterchildren(): c.attrib['flag']='Keep' else: pass for e in doc.xpath('//*[attribute::flag]'): if e.attrib['flag']!='Keep': parent=e.getparent() parent.remove(e) else: pass print(le.tostring(doc)) ##This part writes the pruned down .xml to a file## with open('test.xml', 'w') as t: for line in le.tostring(doc): t.write(line) t.close
这个丑陋的解决方案涉及大量的数据迭代,我怀疑这不是完成此工作的最有效方法,但它确实有效。
目前尚不清楚您要执行的操作,但是此代码会产生您想要的输出:
from lxml import etree as ET def filter_by_itemid(doc, idlist): rowset = doc.xpath("/api/result/rowset[@name='assets']")[0] for elem in rowset.getchildren(): if int(elem.get("itemID")) not in idlist: rowset.remove(elem) return doc doc = ET.parse("test.xml") filter_by_itemid(doc, [1004072840841]) print(ET.tostring(doc))