2017-07-12 27 views
1

我有这种结构的XML文件:的Python读取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的

+0

不应该''终止标记在文件的末尾?因为_XML_文件应该只包含__one__ _root_节点。 – CristiFati

+0

访问'熊猫数据框? – mzjn

+0

@mzjn yes visit = pandas.DataFrame() – Safariba

回答

2

下面是一段代码,从XML获取数据:

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__": 
    main() 

注意

  • 它对待XML在树状方式,所以它地图(如果您愿意)xml(如果xml结构发生变化,代码应该是ad艾普特以及)
  • 它的设计是一般:<产品><访问>get_node_data可能有2个孩子的节点上调用。在我们的例子中,它是根节点本身,但在现实世界中可能有一系列这样的节点,每个节点都有我上面列出的两个孩子
  • 它的设计是对错误友好的,所以如果xml不完整,它将获得尽可能多的数据;我选择了在一个本(贪婪)的方式,当它遇到它只是抛出一个异常的错误
  • ,因为我没有与pandas工作,而不是填充物我简单地返回一个的Python字典json);我认为它转换为DataFrame应该不难
  • 我已经与Python2.7Python3.5

输出(含2个键的字典)运行 - 为缩进可读性:

  • SERIALNUMBER - 序列号(显然)
  • 访问(因为它是一本字典,我只好把这个数据一键“下”) - 字典列表从<访问>节点
{'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。现在,在访问列表中的每个条目将具有键,其值将是由来自各部分节点(不是用于提供XML的情况下)提取的字典的列表。

+1

在这段代码中,当每个访问有多个部分时,只返回最后一部分。它不会为每次访问返回所有部件。 – Safariba

+2

的确如此。我认为每个_visit_只能有一个_part_节点(如例子_xml_)。你想要它处理多个_part_节点吗? (改变是微不足道的) – CristiFati

+1

是的,我想处理多个部分,我在处理字典方面经验不足,你能帮助我吗?谢谢。 – Safariba

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') 

希望这将有助于ü。