2013-03-01 81 views
2

我有一个非常大的.xml文件,我试图做一个新的.xml文件,只是这个大文件的一小部分内容。我想指定一个属性(在我的情况下,一个itemID),并给它一些特定的值,然后它会除去那些具有这些itemIDs和他们的孩子的元素的所有元素。Python XML删除一些元素和他们的孩子,但保留特定元素和他们的孩子

我大.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> 

这个文件大约有90000行,大约是9兆字节。

请注意如何有itemIDs和一些项目类型可以(但并不总是)有更多的项目里面,这些孩子也有自己的itemID。我试图获得一些特定的itemID和他们的孩子,而忽略了所有其他的。

我使用this answer的代码,它让我非常接近。除了它遗漏了我使用的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]行属性(以便我可以使用xPath来引用特定的itemID及其子元素时使用此xml文件)。

所以我的主要问题是关于如何包含子在我的结果.xml中搜索的itemID。我的次要问题是关于如何做到这一点的不止一个ITEMID同时

UPDATE(以使所得.xml文件可能会夺走一切,但这些itemIDs和他们的孩子。):丑陋的解决方案

我发现elem.attrib.pop('itemID')部分是取出itemID的部分,因为我想要多个itemID并且他们的子元素仍然存在,所以我需要保留这个元素,所以我拿出了这部分。我试图找到一种方法来跳过与我正在寻找的itemID的行的孩子,我想到的是用一个属性标记每个人,然后我可以重新搜索并删除所有那些有这个属性。我不需要flag属性来处理我正在做的事情,所以我继续前进并将其用于此目的(因为尝试引入新属性会遇到重要错误,当我尝试对它们进行迭代时)。孩子们还不够,我还必须给孩子的孩子贴上标签。

这里是我的丑陋的解决方案:

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 

这丑陋的解决方案涉及到很多遍历数据,是的,我怀疑,远远的能完成这一操作的最有效的方式,但它确实工作。

回答

3

这不是很清楚究竟你以后,但是这个代码会产生你说你想输出:

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)) 
+0

谢谢你,那个作品!对不起,我正在寻找什么,但你确实弄清楚了 - 这个工作很好!我喜欢如何将'filter_by_itemid'(doc,[1004072840841])'行扩展为像'filter_by_itemid(doc,[1005683240494,1004072840841])'这样的行,它仍然有效(即我可以在idlist中使用多个id)。是一个令人愉快的简单和优雅的解决方案:) – Qanthelas 2013-03-09 05:19:33

+0

很酷,很高兴它帮助。 – spiralx 2013-03-15 14:38:41

相关问题