簡體   English   中英

如何使用 Python ElementTree 從 XML 文件中的不同子級元素中提取相關屬性

[英]how can I extract related attributes from different child level elements from XML file with Python ElementTree

我是 Python 的新手,對 XML 解析有些陌生,我正在努力尋找正確的代碼算法來提取我需要的數據,同時保持子元素\屬性之間的關系。

XML 來自音頻錄制軟件應用程序。 它為產品的多個方面定義了配置(所以很大)。 我想從文件的一小部分中提取一些配置項以供外部使用。

以下示例的父路徑是:./Presets/rootObjects/root/list/item[]

<item>
   <string name="ID" value="InBusConfig" wide="true"/>
   <string name="Name" value="InBusConfig" wide="true"/>
   <list name="Items" type="obj">
      <obj class="FPreset" ID="1511747008">
         <string name="Name" value="Drum &amp; Bass Beds" wide="true"/>
         <member name="Object">
            <list name="Busses" type="list">
               <item>
                  <string ***name="BusName" value="Kick In"*** wide="true"/>
                  <int name="SpeakerArr" value="0"/>
                  <list name="Connections" type="list">
                     <item>
                        <string ***name="PortId" value="I|Focusrite USB ASIO|Input 3"*** wide="true"/>
                        <int name="Speaker" value="0"/>
                     </item>
                  </list>
               </item>
...

               <item>
                  <string name="BusName" value="Live Room" wide="true"/>
                  <int name="SpeakerArr" value="0"/>
                  <list name="Connections" type="list">
                     <item>
                        <string name="PortId" value="I|Focusrite USB ASIO|Digital 10" wide="true"/>
                        <int name="Speaker" value="0"/>
                     </item>
                  </list>
               </item>
            </list>
            <int name="Default Bus Index" value="0"/>
         </member>
         <int name="Unrenamed" value="1"/>
      </obj>
   </list>
</item>

我需要提取(並保持相關)的項目是“BusName”屬性和相應的“PortId”屬性,它是名為“connections”的“list”元素的子元素。

對於我的測試配置中存在的 13 個項目,我想將 output 此數據作為 csv(或 JSON)文件用於另一個工具。

我想要這個 output 的格式在理想情況下類似於:(我已經用“-”替換了 pipe 字符,因為它弄亂了表格格式)

總線名稱 端口號
起作用 I - Focusrite USB ASIO - 輸入 3
直播間 I - Focusrite USB ASIO - 數字 10

我沒有提供任何 py 代碼,因為到目前為止我嘗試過的任何東西都不足以提出一個特定的問題。 因此,我請求有關如何解決此問題的更一般性的幫助。

我會對算法、偽代碼甚至一些 python 特定代碼/功能等感到滿意。

任何方向真的會比我現在擁有的更多。

提前致謝。

我建議不要使用內置的 ElementTree,而是使用像lxmlBeautifulSoup4這樣的 package 。

編輯:這純粹是一個偏好問題。 正如@barny 正確指出的那樣,使用xml.etree.ElementTree可以實現完全相同的事情。

這是lxml的嘗試:

from lxml import etree

data = """
<item>
   <string name="ID" value="InBusConfig" wide="true"/>
   <string name="Name" value="InBusConfig" wide="true"/>
   <list name="Items" type="obj">
      <obj class="FPreset" ID="1511747008">
         <string name="Name" value="Drum &amp; Bass Beds" wide="true"/>
         <member name="Object">
            <list name="Busses" type="list">
               <item>
                  <string name="BusName" value="Kick In" wide="true"/>
                  <int name="SpeakerArr" value="0"/>
                  <list name="Connections" type="list">
                     <item>
                        <string name="PortId" value="I|Focusrite USB ASIO|Input 3" wide="true"/>
                        <int name="Speaker" value="0"/>
                     </item>
                  </list>
               </item>
               <item>
                  <string name="BusName" value="Live Room" wide="true"/>
                  <int name="SpeakerArr" value="0"/>
                  <list name="Connections" type="list">
                     <item>
                        <string name="PortId" value="I|Focusrite USB ASIO|Digital 10" wide="true"/>
                        <int name="Speaker" value="0"/>
                     </item>
                  </list>
               </item>
            </list>
            <int name="Default Bus Index" value="0"/>
         </member>
         <int name="Unrenamed" value="1"/>
      </obj>
   </list>
</item>

"""

root = etree.fromstring(data)

buses = root.xpath('//item[string/@name="BusName"]')

for bus in buses:
    bus_name = bus.find('string').get('value')
    port_id = bus.xpath('list/item/string/@value')[0]
    pair = (bus_name, port_id,)
    print(pair)

這里的總體思路是,它使用 xpath 來查找<item> ,其中有一個<string name="BusName">
從該項目需要:

  • <string>元素的value屬性。
  • list/item/string子項的value屬性的第一個匹配項(因為 xpath 返回一個列表)。
    (如果此列表中有更多項目,您需要在此處進行調整)

Note: I'm just creating a tuple called pair , but obviously you can also store these variables in a dataframe ( pandas ), or wirte it directly to json or csv (see eg here: https://realpython.com/python- .csv/ )。

不知道為什么@balduin 推薦 lxml/avoids ElementTree - 它工作得很好。

與 lxml 相比,ElementTree 對 xpath 的支持有限,但它對於許多任務來說已經足夠了,並且避免需要外部 package 通常很有幫助。 文檔在這里: https://docs.python.org/3/library/xml.etree.elementtree.html?highlight=findall#xml

該頁面上方有一些 xpath 示例

import lxml.etree as ETL
import xml.etree.ElementTree as ET

data = """
<item>
   <string name="ID" value="InBusConfig" wide="true"/>
   <string name="Name" value="InBusConfig" wide="true"/>
   <list name="Items" type="obj">
      <obj class="FPreset" ID="1511747008">
         <string name="Name" value="Drum &amp; Bass Beds" wide="true"/>
         <member name="Object">
            <list name="Busses" type="list">
               <item>
                  <string name="BusName" value="Kick In" wide="true"/>
                  <int name="SpeakerArr" value="0"/>
                  <list name="Connections" type="list">
                     <item>
                        <string name="PortId" value="I|Focusrite USB ASIO|Input 3" wide="true"/>
                        <int name="Speaker" value="0"/>
                     </item>
                  </list>
               </item>
               <item>
                  <string name="BusName" value="Live Room" wide="true"/>
                  <int name="SpeakerArr" value="0"/>
                  <list name="Connections" type="list">
                     <item>
                        <string name="PortId" value="I|Focusrite USB ASIO|Digital 10" wide="true"/>
                        <int name="Speaker" value="0"/>
                     </item>
                  </list>
               </item>
            </list>
            <int name="Default Bus Index" value="0"/>
         </member>
         <int name="Unrenamed" value="1"/>
      </obj>
   </list>
</item>

"""

# lxml
root = ETL.fromstring(data)

buses = root.xpath('//item[string/@name="BusName"]')

for bus in buses:
    bus_name = bus.find('string').get('value')
    port_id = bus.xpath('list/item/string/@value')[0]
    pair = (bus_name, port_id,)
    print(pair)

# ElementTree
root1 = ET.fromstring(data)

for el in root1.findall('.//item/string[@name="BusName"]/..'):
    bus_name = el.find('./string').get('value')
    port_id=el.find("./list/item/string").get('value')
    pair=(bus_name,port_id)
    print(pair)

Output lxml:

('Kick In', 'I|Focusrite USB ASIO|Input 3')
('Live Room', 'I|Focusrite USB ASIO|Digital 10')

Output 元素樹:

('Kick In', 'I|Focusrite USB ASIO|Input 3')
('Live Room', 'I|Focusrite USB ASIO|Digital 10')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM