横向对比分析Python解析XML的四种方式


Posted in Python onMarch 30, 2016

在最初学习PYTHON的时候,只知道有DOM和SAX两种解析方法,但是其效率都不够理想,由于需要处理的文件数量太大,这两种方式耗时太高无法接受。

在网络搜索后发现,目前应用比较广泛,且效率相对较高的ElementTree也是一个比较多人推荐的算法,于是拿这个算法来实测对比,ElementTree也包括两种实现,一个是普通ElementTree(ET),一个是ElementTree.iterparse(ET_iter)。

本文将对DOM、SAX、ET、ET_iter四种方式进行横向对比,通过处理相同文件比较各个算法的用时来评估其效率。

程序中将四种解析方法均写为函数,在主程序中分别调用,来评估其解析效率。

解压后的XML文件内容示例为:

横向对比分析Python解析XML的四种方式

主程序函数调用部分代码为:

print("文件计数:%d/%d." % (gz_cnt,paser_num))
  str_s,cnt = dom_parser(gz)
  #str_s,cnt = sax_parser(gz)
  #str_s,cnt = ET_parser(gz)
  #str_s,cnt = ET_parser_iter(gz)
  output.write(str_s)
  vs_cnt += cnt

在最初的函数调用中函数返回两个值,但接收函数调用值时用两个变量分别调用,导致每个函数都要执行两次,之后修改为一次调用两个变量接收返回值,减少了无效调用。

1、DOM解析

函数定义代码:

def dom_parser(gz):
  import gzip,cStringIO
  import xml.dom.minidom
  
  vs_cnt = 0
  str_s = ''
  file_io = cStringIO.StringIO()
  xm = gzip.open(gz,'rb')
  print("已读入:%s.\n解析中:" % (os.path.abspath(gz)))
  doc = xml.dom.minidom.parseString(xm.read())
  bulkPmMrDataFile = doc.documentElement
  #读入子元素
  enbs = bulkPmMrDataFile.getElementsByTagName("eNB")
  measurements = enbs[0].getElementsByTagName("measurement")
  objects = measurements[0].getElementsByTagName("object")
  #写入csv文件
  for object in objects:
    vs = object.getElementsByTagName("v")
    vs_cnt += len(vs)
    for v in vs:
      file_io.write(enbs[0].getAttribute("id")+' '+object.getAttribute("id")+' '+\
      object.getAttribute("MmeUeS1apId")+' '+object.getAttribute("MmeGroupId")+' '+object.getAttribute("MmeCode")+' '+\
      object.getAttribute("TimeStamp")+' '+v.childNodes[0].data+'\n') #获取文本值
  str_s = (((file_io.getvalue().replace(' \n','\r\n')).replace(' ',',')).replace('T',' ')).replace('NIL','')
  xm.close()
  file_io.close()
  return (str_s,vs_cnt)

程序运行结果:

**************************************************
程序处理启动。
输入目录为:/tmcdata/mro2csv/input31/。
输出目录为:/tmcdata/mro2csv/output31/。
输入目录下.gz文件个数为:12,本次处理其中的12个。
**************************************************
文件计数:1/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_234598_20160224060000.xml.gz.
解析中:
文件计数:2/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_233798_20160224060000.xml.gz.
解析中:
文件计数:3/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_123798_20160224060000.xml.gz.
解析中:
………………………………………
文件计数:12/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_235598_20160224060000.xml.gz.
解析中:
VS行计数:177849,运行时间:107.077867,每秒处理行数:1660。
已写入:/tmcdata/mro2csv/output31/mro_0001.csv。

**************************************************
程序处理结束。
由于DOM解析需要将整个文件读入内存,并建立树结构,其内存消耗和时间消耗都比较高,但其优点在于逻辑简单,不需要定义回调函数,便于实现。

2、SAX解析

函数定义代码:

def sax_parser(gz):
  import os,gzip,cStringIO
  from xml.parsers.expat import ParserCreate

  #变量声明
  d_eNB = {}
  d_obj = {}
  s = ''
  global flag 
  flag = False
  file_io = cStringIO.StringIO()
  
  #Sax解析类
  class DefaultSaxHandler(object):
    #处理开始标签
    def start_element(self, name, attrs):
      global d_eNB
      global d_obj
      global vs_cnt
      if name == 'eNB':
        d_eNB = attrs
      elif name == 'object':
        d_obj = attrs
      elif name == 'v':
        file_io.write(d_eNB['id']+' '+ d_obj['id']+' '+d_obj['MmeUeS1apId']+' '+d_obj['MmeGroupId']+' '+d_obj['MmeCode']+' '+d_obj['TimeStamp']+' ')
        vs_cnt += 1
      else:
        pass
    #处理中间文本
    def char_data(self, text):
      global d_eNB
      global d_obj
      global flag
      if text[0:1].isnumeric():
        file_io.write(text)
      elif text[0:17] == 'MR.LteScPlrULQci1':
        flag = True
        #print(text,flag)
      else:
        pass
    #处理结束标签
    def end_element(self, name):
      global d_eNB
      global d_obj
      if name == 'v':
        file_io.write('\n')
      else:
        pass
  
  #Sax解析调用
  handler = DefaultSaxHandler()
  parser = ParserCreate()
  parser.StartElementHandler = handler.start_element
  parser.EndElementHandler = handler.end_element
  parser.CharacterDataHandler = handler.char_data
  vs_cnt = 0
  str_s = ''
  xm = gzip.open(gz,'rb')
  print("已读入:%s.\n解析中:" % (os.path.abspath(gz)))
  for line in xm.readlines():
    parser.Parse(line) #解析xml文件内容
    if flag:
      break
  str_s = file_io.getvalue().replace(' \n','\r\n').replace(' ',',').replace('T',' ').replace('NIL','')  #写入解析后内容
  xm.close()
  file_io.close()
  return (str_s,vs_cnt)

程序运行结果:

**************************************************
程序处理启动。
输入目录为:/tmcdata/mro2csv/input31/。
输出目录为:/tmcdata/mro2csv/output31/。
输入目录下.gz文件个数为:12,本次处理其中的12个。
**************************************************
文件计数:1/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_234598_20160224060000.xml.gz.
解析中:
文件计数:2/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_233798_20160224060000.xml.gz.
解析中:
文件计数:3/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_123798_20160224060000.xml.gz.
解析中:
.........................................
文件计数:12/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_235598_20160224060000.xml.gz.
解析中:
VS行计数:177849,运行时间:14.386779,每秒处理行数:12361。
已写入:/tmcdata/mro2csv/output31/mro_0001.csv。

**************************************************
程序处理结束。
SAX解析相比DOM解析,运行时间大幅缩短,由于SAX采用逐行解析,对于处理较大文件其占用内存也少,因此SAX解析是目前应用较多的一种解析方法。其缺点在于需要自己实现回调函数,逻辑较为复杂。

3、ET解析

函数定义代码:

def ET_parser(gz):
  import os,gzip,cStringIO
  import xml.etree.cElementTree as ET

  vs_cnt = 0
  str_s = ''
  file_io = cStringIO.StringIO()
  xm = gzip.open(gz,'rb')
  print("已读入:%s.\n解析中:" % (os.path.abspath(gz)))
  tree = ET.ElementTree(file=xm)
  root = tree.getroot()
  for elem in root[1][0].findall('object'):
      for v in elem.findall('v'):
          file_io.write(root[1].attrib['id']+' '+elem.attrib['TimeStamp']+' '+elem.attrib['MmeCode']+' '+\
          elem.attrib['id']+' '+ elem.attrib['MmeUeS1apId']+' '+ elem.attrib['MmeGroupId']+' '+ v.text+'\n')
      vs_cnt += 1
  str_s = file_io.getvalue().replace(' \n','\r\n').replace(' ',',').replace('T',' ').replace('NIL','')  #写入解析后内容
  xm.close()
  file_io.close()
  return (str_s,vs_cnt)

程序运行结果:

**************************************************
程序处理启动。
输入目录为:/tmcdata/mro2csv/input31/。
输出目录为:/tmcdata/mro2csv/output31/。
输入目录下.gz文件个数为:12,本次处理其中的12个。
**************************************************
文件计数:1/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_234598_20160224060000.xml.gz.
解析中:
文件计数:2/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_233798_20160224060000.xml.gz.
解析中:
文件计数:3/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_123798_20160224060000.xml.gz.
解析中:
...........................................
文件计数:12/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_235598_20160224060000.xml.gz.
解析中:
VS行计数:177849,运行时间:4.308103,每秒处理行数:41282。
已写入:/tmcdata/mro2csv/output31/mro_0001.csv。

**************************************************
程序处理结束。
相较于SAX解析,ET解析时间更短,并且函数实现也比较简单,所以ET具有类似DOM的简单逻辑实现且匹敌SAX的解析效率,因此ET是目前XML解析的首选。

4、ET_iter解析

函数定义代码:

def ET_parser_iter(gz):
  import os,gzip,cStringIO
  import xml.etree.cElementTree as ET

  vs_cnt = 0
  str_s = ''
  file_io = cStringIO.StringIO()
  xm = gzip.open(gz,'rb')
  print("已读入:%s.\n解析中:" % (os.path.abspath(gz)))
  d_eNB = {}
  d_obj = {}
  i = 0
  for event,elem in ET.iterparse(xm,events=('start','end')):
    if i >= 2:
      break    
    elif event == 'start':
          if elem.tag == 'eNB':
              d_eNB = elem.attrib
          elif elem.tag == 'object':
        d_obj = elem.attrib
      elif event == 'end' and elem.tag == 'smr':
      i += 1
    elif event == 'end' and elem.tag == 'v':
      file_io.write(d_eNB['id']+' '+d_obj['TimeStamp']+' '+d_obj['MmeCode']+' '+d_obj['id']+' '+\
      d_obj['MmeUeS1apId']+' '+ d_obj['MmeGroupId']+' '+str(elem.text)+'\n')
          vs_cnt += 1
      elem.clear()
  str_s = file_io.getvalue().replace(' \n','\r\n').replace(' ',',').replace('T',' ').replace('NIL','')  #写入解析后内容
  xm.close()
  file_io.close()
  return (str_s,vs_cnt)

程序运行结果:

**************************************************
程序处理启动。
输入目录为:/tmcdata/mro2csv/input31/。
输出目录为:/tmcdata/mro2csv/output31/。
输入目录下.gz文件个数为:12,本次处理其中的12个。
**************************************************
文件计数:1/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_234598_20160224060000.xml.gz.
解析中:
文件计数:2/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_233798_20160224060000.xml.gz.
解析中:
文件计数:3/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_123798_20160224060000.xml.gz.
解析中:
...................................................
文件计数:12/12.
已读入:/tmcdata/mro2csv/input31/TD-LTE_MRO_NSN_OMC_235598_20160224060000.xml.gz.
解析中:
VS行计数:177849,运行时间:3.043805,每秒处理行数:58429。
已写入:/tmcdata/mro2csv/output31/mro_0001.csv。

**************************************************
程序处理结束。
在引入了ET_iter解析后,解析效率比ET提升了近50%,而相较于DOM解析更是提升了35倍,在解析效率提升的同时,由于其采用了iterparse这个循序解析的工具,其内存占用也是比较小的。

所以,小伙伴们,请好好利用这几种工具吧。
以上就是本文的全部内容,希望对大家的学习有所帮助。

Python 相关文章推荐
寻找网站后台地址的python脚本
Sep 01 Python
python多重继承新算法C3介绍
Sep 28 Python
Python中转换角度为弧度的radians()方法
May 18 Python
python基本语法练习实例
Sep 19 Python
Python实现的爬取网易动态评论操作示例
Jun 06 Python
python一键去抖音视频水印工具
Sep 14 Python
学习和使用python的13个理由
Jul 30 Python
Python一键查找iOS项目中未使用的图片、音频、视频资源
Aug 12 Python
python提取照片坐标信息的实例代码
Aug 14 Python
python调用c++返回带成员指针的类指针实例
Dec 12 Python
TensorFlow2.0:张量的合并与分割实例
Jan 19 Python
python如何查看安装了的模块
Jun 23 Python
python简单实现刷新智联简历
Mar 30 #Python
详解Python迭代和迭代器
Mar 28 #Python
Python读大数据txt
Mar 28 #Python
Python常用的爬虫技巧总结
Mar 28 #Python
Python对数据库操作
Mar 28 #Python
Python字符串切片操作知识详解
Mar 28 #Python
python Django框架实现自定义表单提交
Mar 25 #Python
You might like
杏林同学录(九)
2006/10/09 PHP
PHP实现抓取迅雷VIP账号的方法
2015/07/30 PHP
php判断是否为ajax请求的方法
2016/11/29 PHP
php面向对象之反射功能与用法分析
2017/03/29 PHP
php layui实现前端多图上传实例
2019/07/30 PHP
不错的asp中显示新闻的功能
2006/10/13 Javascript
学习YUI.Ext 第四天--对话框Dialog的使用
2007/03/10 Javascript
用dtree实现树形菜单 dtree使用说明
2011/10/17 Javascript
js数组中如何随机取出一个值
2014/06/13 Javascript
JavaScript小技巧整理
2015/12/30 Javascript
javascript函数自动执行常用方法汇总
2016/03/28 Javascript
关于JavaScript和jQuery的类型判断详解
2016/10/08 Javascript
详解JS-- 浮点数运算处理
2016/11/28 Javascript
利用select实现年月日三级联动的日期选择效果【推荐】
2016/12/13 Javascript
vuejs如何配置less
2017/04/25 Javascript
关于webpack代码拆分的解析
2017/07/20 Javascript
JS实现的集合去重,交集,并集,差集功能示例
2018/03/13 Javascript
jQuery实现高度灵活的表单验证功能示例【无UI】
2020/04/30 jQuery
Python编程中的文件读写及相关的文件对象方法讲解
2016/01/19 Python
python爬取网页转换为PDF文件
2018/06/07 Python
Python实现注册、登录小程序功能
2018/09/21 Python
pandas实现将日期转换成timestamp
2019/12/07 Python
django使用channels实现通信的示例
2020/10/19 Python
办公室文秘自我鉴定
2013/09/21 职场文书
工程专业求职自荐书范文
2014/02/08 职场文书
欢度春节标语
2014/07/01 职场文书
环境保护建议书
2014/08/26 职场文书
九一八事变演讲稿范文
2014/09/14 职场文书
大四优秀党员个人民主评议
2014/09/19 职场文书
2015年大学团支部工作总结
2015/05/13 职场文书
楚门的世界观后感
2015/06/03 职场文书
网络妈妈观后感
2015/06/08 职场文书
新员工实习期个人工作总结
2015/10/15 职场文书
2019大学生预备党员转正思想汇报
2019/06/21 职场文书
mysql 如何获取两个集合的交集/差集/并集
2021/06/08 MySQL
Java结构型设计模式之组合模式详解
2022/09/23 Java/Android