Ho для анализа и получения элемента xml с использованием фрейма данных Python

#python #pandas #dataframe

Вопрос:

Это моя XML-строка, я получаю это как сообщение, так что это не файл

 <?xml version="1.0" encoding="UTF-8"?>
<OperationStatus xmlns:ns2="summaries">
   <EventId>123456</EventId>
   <notificationId>123456</notificationId>
   <userDetails>
      <clientId>client_1</clientId>
      <userId>user_1</userId>
      <groupIds>
         <groupId>123456</groupId>
         <groupId>123457</groupId>
      </groupIds>
   </userDetails>
</OperationStatus>
 

Я хочу получить вывод в формате ниже

 message,code,Id
 

Я упомянул только три элемента, но у меня может быть гораздо больше элементов .

Вот как я пытаюсь, но не получаю точного результата

Я начал изучать Python, так что извините меня за глупые ошибки

     from __future__ import print_function
    import pandas as pd
    
    def lambda_handler():
    
    import xml.etree.ElementTree as et
    
    xtree = et.parse('''<?xml version="1.0" encoding="UTF-8"?>
<OperationStatus xmlns:ns2="summaries">
   <EventId>123456</EventId>
   <notificationId>123456</notificationId>
   <userDetails>
      <clientId>client_1</clientId>
      <userId>user_1</userId>
      <groupIds>
         <groupId>123456</groupId>
         <groupId>123457</groupId>
      </groupIds>
   </userDetails>
</OperationStatus>''')
    xroot = xtree.getroot()
    
    df_cols = ["message", "code", "Id"]
    rows = []
    
    for node in xroot:
        s_name = node.attrib.get("message")
        s_mail = node.find("code").text if node is not None else None
        s_grade = node.find("Id").text if node is not None else None


lambda_handler()
 

Комментарии:

1. Как создается ваш фрейм данных? Какова цель lambda_handler этого ?

Ответ №1:

вы можете попробовать использовать XPath, так будет проще получить нужные данные

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

xtree = et.fromstring("""<?xml version="1.0" encoding="UTF-8"?>
<name xmlns:ns2="summaries">
   <message>5jb10x5rf7sp1fov5msgoof7r</message>
   <code>COMPLETED</code>
   <Id>dfkjlhgd98568y</Id>
</name>""")

keys = ["message", "code", "Id"]

data = {k: [xtree.find(".//" k).text] for k in keys}

print(pd.DataFrame(data))
# Outputs:
#                      message       code              Id
# 0  5jb10x5rf7sp1fov5msgoof7r  COMPLETED  dfkjlhgd98568y
 

Комментарии:

1. Это не работает, если у нас есть два уровня xml-элемента . я нашел это простое решение, но должно ли .// оно играть роль, если у нас многослойный xml ?

Ответ №2:

Это тот результат, которого вы хотите?

 # !pip install xmltodict
import xmltodict
xml = """
<name xmlns:ns2="summaries">
   <message>5jb10x5rf7sp1fov5msgoof7r</message>
   <code>COMPLETED</code>
   <Id>dfkjlhgd98568y</Id>
</name>
"""

d = xmltodict.parse(xml)
print(d['name']['message'])
print(d['name']['code'])
print(d['name']['Id'])
 

Выход

 5jb10x5rf7sp1fov5msgoof7r
COMPLETED
dfkjlhgd98568y
 

Более xmltodict подробная информация на сайте https://github.com/martinblech/xmltodict

Ответ №3:

Учитывая вашу строку:

 your_string='''
<?xml version="1.0" encoding="UTF-8"?>
<name xmlns:ns2="summaries">
   <message>5jb10x5rf7sp1fov5msgoof7r</message>
   <code>COMPLETED</code>
   <Id>dfkjlhgd98568y</Id>
</name>'''
 

Поскольку это строка, вы бы использовали .fromstring (), а не .parse(). Это автоматически находит корневой узел name для вас (т. Е. Не нужно звонить .getroot() ):

 root = et.fromstring(your_string)

>>> root
<Element 'name' at 0x1050f51d0>
 

Как только у вас будет структура данных с name корнем, вы можете либо выполнить итерацию по подэлементам:

 df_cols = ["message", "code", "Id"]

for node in root:
    if node.tag in df_cols:
        print({node.tag:node.text})
 

С принтами:

 {'message': '5jb10x5rf7sp1fov5msgoof7r'}
{'code': 'COMPLETED'}
{'Id': 'dfkjlhgd98568y'}
 

Или вы можете использовать запрос xpath для поиска каждого интересующего элемента:

 for k in df_cols:       
    print({k:root.find(f'./{k}').text})  
# same output
 

Теперь, поскольку фрейм данных может быть построен с помощью {key:[list_of_elements],...} , вы можете построить этот тип диктата из того, что мы построили здесь:

 df=pd.DataFrame({k:[root.find(f'./{k}').text] for k in df_cols})
 

Если у вас несколько элементов, используйте findall :

 df=pd.DataFrame({k:[x.text for x in root.findall(f'./{k}')] for k in df_cols})