2

非常に大きな .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 とその子を取得して、他のすべてを除外しようとしています。

この回答のコードを使用しましたが、かなり近づきました。私が使用した 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]ます (この xml ファイルを使用するときに、xPath を使用して特定の itemID とその子を参照できるようにするためです)。

したがって、私の主な質問は、検索した itemID の子を結果の .xml に含める方法についてです。私の 2 番目の質問は、複数の itemID に対して同時にこれを行う方法についてです (結果の .xml ファイルがそれらの itemID とその子以外のすべてを削除するようにします)。

更新:醜い解決策

その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

この見苦しい解決策は、データを何度も繰り返し処理する必要があり、これを実行するための最も効率的な方法とはほど遠いと思いますが、うまくいきます。

4

1 に答える 1

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))
于 2013-03-08T10:49:19.703 に答える