使用Python将数据从XML树提取到pandas / csv

时间:2018-11-22 09:40:39

标签: python xml pandas export-to-csv

我对某些XML文件有问题。 关于数据,我不能说太多,因为它是用于工作的,我不想惹麻烦!从一个巨大的XML文件(123091行代码)中,我只需要来自7个标记的数据(如果有这样的话)。 我正在尝试提取特定数据,但是在尝试存储到熊猫或csv中时遇到了一些情况。 我找到了一种可以提取一些信息的方法,例如:

for info in root.iter('ArtistName'):
   print(info.text)

上面的代码将为我提供来自该XML标签的数据中的艺术家。这是我的Jupyter Notebook的一小部分,带有上述代码行的输出:

Various Artists
Various Artists
Various Artists
Various Artists
Various Artists
Cream
Various Artists
Various Artists
Various Artists
Various Artists
Various Artists
Fleetwood Mac
Fleetwood Mac
Linkin Park
Lynyrd Skynyrd
Fleetwood Mac
Eric Clapton
The Black Keys
Tegan And Sara

然后,我遇到了问题,因为在下面的代码中,我不能说得更好,或者说得更好,我不知道如何遍历XML中的每个标记以提取数据。以下是尝试:

import xml.etree.ElementTree as ET
import pandas as pd

tree = ET.parse("filename.xml")
root = tree.getroot()
dfcols = ['IRC', 'IRC2', 'Artist', 'Song', 'Units', 'PPD', 'TerritoryCode']
df_xml = pd.DataFrame(columns = dfcols)

for i in root.iter(tree):
   df_xml = df_xml.append(pd.Series(index=dfcols), ignore_index=True)

df_xml.head()

以上代码的结果为:

 IRC IRC2 Artist Song Units PPD TerritoryCode

哪个是我要创建的文件的标题。 我找不到将所需信息输入这些列的方法。

我也尝试过:

def getValOfNode(node):
    return node.text if node is not None else None


def main():

    dfcols = ['IRC', 'IRC2', 'Artist', 'Song', 'Units', 'PPD', 'TerritoryCode']
    df_xml = pd.DataFrame(columns = dfcols)

    for node in tree:
        IRC = node.find('IRC')
        IRC2 = node.find('ICPN')
        Artist = node.find('rtistName')
        Song = node.find('Title')
        Units = node.find('ConsumerSales')
        PPD = node.find('Amount')
        TerritoryCode = node.find('TerritoryCode')

        df_xml = df_xml.append(
            pd.Series([getValOfNode(IRC), getValOfNode(IRC2), getValOfNode(Artist), getValOfNode(Song), getValOfNode(Units), getValOfNode(PPD), getValOfNode(TerritoryCode)], index=dfcols), ignore_index=True)

    print(df_xml)


main()

我收到此错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-1f816143f9e4> in <module>()
     23 
     24 
---> 25 main()

<ipython-input-5-1f816143f9e4> in main()
      8     df_xml = pd.DataFrame(columns = dfcols)
      9 
---> 10     for node in tree:
     11         IRC = node.find('IRC')
     12         IRC2 = node.find('ICPN')

TypeError: 'ElementTree' object is not iterable

运行时,地区代码也有问题:

for info in root.iter('TerritoryCode'):
   print(info.text)

它打印出这些领土,但是,由于它们是重复的(我不知道如何解释),所以我真的需要所有这些,而不仅仅是每个。如果这样的话。 这就是我得到的:

AE
AR
AT
AU
AW
BE
BG
BO
BR
BY
CA
CH
CL
CN
CO
CR
CY
CZ
DE
DK
DO
DZ
EC
EE
EG
ES
FI
FR
GB
GL
GR
GT
HK
HN

这就是我需要的:

AD
AD
AE
AE
AE
AE
AE
AE,

等等。

有人可以帮我吗? 非常感激。

祝你有美好的一天:)

1 个答案:

答案 0 :(得分:0)

如前所述,您所需的节点位于XML的不同级别,因此每个数据项的路径表达式都不同。另外,您需要遍历两个重复级别: SalesToRecordCompanyByTerritory ReleaseTransactionsToRecordCompany

因此,考虑在嵌套的for循环中进行解析。与其在循环内增加数据框架,不如构建一个字典列表,您可以将其传递给循环外的熊猫的DataFrame()构造函数。通过这种方法,您可以将字典键迁移为列,将元素迁移为数据。

以下使用链式find()调用,长相对或短绝对路径来导航嵌套级别并检索相应的元素文本值。请注意,所有解析都是相对于具有父对象terr和子对象rls的循环节点的。

import xml.etree.ElementTree as ET
import pandas as pd

tree = ET.parse("file.xml")

data = []
for terr in tree.findall('.//SalesToRecordCompanyByTerritory'):

    for rls in terr.findall('.//ReleaseTransactionsToRecordCompany'):

        inner = {}

        # DESCENDANTS
        inner['IRC'] = rls.find('./ReleaseId/ISRC').text    
        inner['IRC2'] = rls.find('./ReleaseId/ICPN').text

        # CHILDREN
        inner['Artist'] = rls.find('WMGArtistName').text
        inner['Song'] = rls.find('WMGTitle').text

        # DESCENDANTS
        inner['Units'] = rls.find('./SalesTransactionToRecordCompany/SalesDataToRecordCompany/GrossNumberOfConsumerSales').text    
        inner['PPD'] = rls.find('Deal').find('AmountPayableInCurrencyOfAccounting').text

        # PARENT
        inner['TerritoryCode'] = terr.find('./TerritoryCode').text

        data.append(inner)

df = pd.DataFrame(data)

您可以使用find()缩短.//链和具有绝对路径的较长相对路径:

inner['IRC'] = rls.find('.//ISRC').text    
inner['IRC2'] = rls.find('.//ICPN').text

inner['PPD'] = rls.find('.//AmountPayableInCurrencyOfAccounting').text
inner['Units'] = rls.find('.//GrossNumberOfConsumerSales').text