Извлечение данных из дерева XML в pandas / csv с помощью Python - PullRequest
0 голосов
/ 22 ноября 2018

У меня проблема с некоторыми файлами XML.Я не могу много говорить о данных, потому что это для работы, и я не хочу быть в беде!Из огромного XML-файла, 123091 строк кода, мне нужны только данные из 7 тегов (если это имеет смысл).Я пытаюсь извлечь эти конкретные данные, но у меня возникла небольшая ситуация при попытке сохранить в pandas или csv.Я нашел способ получить некоторую информацию, например:

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

Приведенный выше код даст мне артистов в данных из этого тега XML.Вот небольшая часть моего ноутбука Jupyter с выводом приведенных выше строк кода:

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 голосов
/ 28 ноября 2018

Как уже упоминалось, ваши необходимые узлы находятся на разных уровнях XML, и, следовательно, выражения пути будут отличаться для каждого элемента данных.Кроме того, вам нужно пройти между двумя повторяющимися уровнями: SalesToRecordCompanyByTerritory и ReleaseTransactionsToRecordCompany .

Поэтому рассмотрим разбор во вложенных for циклах.И вместо того, чтобы увеличивать фрейм данных внутри цикла, создайте список словарей, которые вы можете передать в конструктор pandas 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  
...