Python使用相关的子元素读取xml

时间:2017-07-12 06:15:00

标签: python xml pandas xml-parsing elementtree

我有一个具有以下结构的xml文件:

<?DOMParser ?> 
<logbook:LogBook xmlns:logbook="http://www/logbook/1.0"  version="1.2">
<product>
    <serialNumber value="764000606"/>
</product>
<visits>
<visit>
    <general>
        <startDateTime>2014-01-10T12:22:39.166Z</startDateTime>
        <endDateTime>2014-03-11T13:51:31.480Z</endDateTime>
    </general>
    <parts>
        <part number="03081" name="WSSA" index="0016"/>
    </parts>
</visit>
<visit>
<general>
    <startDateTime>2013-01-10T12:22:39.166Z</startDateTime>
    <endDateTime>2013-03-11T13:51:31.480Z</endDateTime>
</general>
<parts>
    <part number="02081" name="PSSF" index="0017"/>
</parts>
</visit>
</visits>
</logbook:LogBook>

我希望这个xml有两个输出:

1-访问包括序列号,所以我写道:

import pandas as pd
import xml.etree.ElementTree as ET
tree = ET.parse(filename)
root=tree.getroot()
visits=pd.DataFrame()
for general in root.iter('general'):
    for child in root.iter('serialNumber'):
        visits=visits.append({'startDateTime':general.find('startDateTime').text ,
                  'endDateTime': general.find('endDateTime').text, 'serialNumber':child.attrib['value'] }, ignore_index=True)

此代码的输出遵循以下数据框:

serialNumber | startDateTime          | endDateTime            
-------------|------------------------|------------------------|
 764000606   |2014-01-10T12:22:39.166Z|2014-03-11T13:51:31.480Z|
 764000606   |2013-03-11T13:51:31.480Z|2013-01-10T12:22:39.166Z|

2部分

对于parts,我希望得到以下输出,以startDateTime区分彼此的访问,并且我想显示与每次访问相关的部分:

 serialNumber | startDateTime|number|name|index|
 -------------|--------------|------|----|-----|

我写的部分:

parts=pd.DataFrame()
for part in root.iter('part'):
    for child in root.iter('serialNumber'):
            parts=parts.append({'index':part.attrib['index'],
                        'znumber':part.attrib['number'],
                        'name': part.attrib['name'], 'serialNumber':child.attrib['value'], 'startDateTime':general.find('startDateTime').text}, ignore_index=True)

这是我从这段代码中得到的:

 index |name|serialNumber| startDateTime          |znumber|
 ------|----|------------|------------------------|-------|
 0016  |WSSA|  764000606 |2013-01-10T12:22:39.166Z| 03081 |
 0017  |PSSF|  764000606 |2013-01-10T12:22:39.166Z| 02081 |

虽然我想要这个:看看startDateTime

 index |name|serialNumber| startDateTime          |znumber|
 ------|----|------------|------------------------|-------|
 0016  |WSSA|  764000606 |2014-01-10T12:22:39.166Z| 03081 |
 0017  |PSSF|  764000606 |2013-01-10T12:22:39.166Z| 02081 |

有什么想法吗? 我正在使用XML ElementTree

2 个答案:

答案 0 :(得分:2)

以下是从 xml 获取数据的示例。

code.py

#!/usr/bin/env python3

import sys
import xml.etree.ElementTree as ET
from pprint import pprint as pp


file_name = "a.xml"


def get_product_sn(product_node):
    for product_node_child in list(product_node):
        if product_node_child.tag == "serialNumber":
            return product_node_child.attrib.get("value", None)
    return None


def get_parts_data(parts_node):
    ret = list()
    for parts_node_child in list(parts_node):
        attrs = parts_node_child.attrib
        ret.append({"number": attrs.get("number", None), "name": attrs.get("name", None), "index": attrs.get("index", None)})
    return ret


def get_visit_node_data(visit_node):
    ret = dict()
    for visit_node_child in list(visit_node):
        if visit_node_child.tag == "general":
            for general_node_child in list(visit_node_child):
                if general_node_child.tag == "startDateTime":
                    ret["startDateTime"] = general_node_child.text
                elif general_node_child.tag == "endDateTime":
                    ret["endDateTime"] = general_node_child.text
        elif visit_node_child.tag == "parts":
            ret["parts"] = get_parts_data(visit_node_child)
    return ret


def get_node_data(node):
    ret = {"visits": list()}
    for node_child in list(node):
        if node_child.tag == "product":
            ret["serialNumber"] = get_product_sn(node_child)
        elif node_child.tag == "visits":
            for visits_node_child in list(node_child):
                ret["visits"].append(get_visit_node_data(visits_node_child))
    return ret


def main():
    tree = ET.parse(file_name)
    root_node = tree.getroot()
    data = get_node_data(root_node)
    pp(data)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

备注

  • 它以树状方式处理 xml ,因此它会映射(如果您愿意) xml (如果 xml 结构变化,代码也应该适应)
  • 它设计为通用的: get_node_data 可以在有2个孩子的节点上调用:产品访问。在我们的例子中,它是根节点本身,但在现实世界中,可能有一系列这样的节点,每个节点都有上面列出的2个孩子
  • 它设计为对错误友好,因此如果 xml 不完整,它将获得尽可能多的数据;我选择了这种(贪婪)方法,当它遇到错误时,它只会引发异常
  • 由于我没有使用 pandas ,而不是填充对象我只返回 Python 字典 JSON );我认为将其转换为 DataFrame 不应该很难
  • 我使用 Python 2.7 Python 3.5
  • 运行它

输出(包含2个键的字典) - 为了便于阅读而缩进:

  • serialNumber - 序列号(显然)
  • 访问(因为它是一本字典,我不得不放置这些数据&#34;在&#34;一个键下) - 一个字典列表,每个字典都包含来自的数据访问节点

<强>输出

(py_064_03.05.04_test0) e:\Work\Dev\StackOverflow\q045049761>"e:\Work\Dev\VEnvs\py_064_03.05.04_test0\Scripts\python.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32

{'serialNumber': '764000606',
 'visits': [{'endDateTime': '2014-03-11T13:51:31.480Z',
             'parts': [{'index': '0016', 'name': 'WSSA', 'number': '03081'}],
             'startDateTime': '2014-01-10T12:22:39.166Z'},
            {'endDateTime': '2013-03-11T13:51:31.480Z',
             'parts': [{'index': '0017', 'name': 'PSSF', 'number': '02081'}],
             'startDateTime': '2013-01-10T12:22:39.166Z'}]}


@ EDIT0 :根据其中一条评论中的请求添加了多个部分节点处理。该功能已移至 get_parts_data 。现在,访问列表中的每个条目都有一个 parts 键,其值将是一个列表,其中包含从每个 part 节点中提取的词典(不是所提供的 xml )的情况。

答案 1 :(得分:0)

尝试以下方法,

import xml.dom.minidom as minidom
doc = minidom.parse('filename')
memoryElem = doc.getElementsByTagName('part')[0]

print memoryElem.getAttribute('number')
print memoryElem.getAttribute('name')
print memoryElem.getAttribute('index')

希望它会对你有所帮助。