在测试工作中,我们经常会遇到需要使用 Python 来读取 XML、Excel、Json 数据的场景,本篇文章把 Python 如何解析和处理这几种数据的做了一个归纳,方便实际使用时候的查询。
大家都知道 XML 是一个标记语言,被设计用来传输和存储数据,日常工作中我们会遇到很多配置文件的格式是 XML,也经常会遇到需要读取和修改 XML 文件的需求。
和 Java 一样,Python 对于 XML 的解析也包括 SAX 和 DOM,另外还有一个轻量级的 ElementTree。这三个库都已经包含在了 Python 标准库里面。可以通过 import xml.sax|dom 来导入相应的库。我们将会介绍 SAX 和 DOM 的优缺点,并以 ElementTree 为库来介绍 Python 对于 XML 文件的操作。
DOM(Document Object Model) 的解析方式,会把整个 XML 文件在内存中解析成一个文件模型(一棵树),按照 XMl 中标签的层级关系来生成相应的节点,通过对树的操作来操作 XML。这样操作的优点是通过树,可以比较简单的遍历和修改节点,适合对于做文件做修改操作。缺点是当 XML 文件比较大的时候,对于内存的开销有压力,解析的时间也会很长。
SAX(Simple API for XML) 的解析方式,会逐行扫描文档,一边扫描一边进行解析。相比于 DOM,SAX 可以在解析过程中随时停止,是一种更快速的解析方法。也不会产生完整的结构对于内存有开销的压力,适合做对于 XML 的读取操作。
ElementTree 我理解的是一个轻量级的 DOM,提供了良好的 API 来方便我们遍历和修改 XML 节点。
ElementTree 库保存在 Lib/xml/etree/ElementTree.py 文件下面,可以用以下的语句来进行导入.(以 Python3 为例)
import xml.etree.ElementTree as ET
本段用到的 XML 文件 sample.xml 内容如下
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
首先,我们需要读入 XML.
tree = ET.parse('sample.xml')
root = tree.getroot()
如果不是需要读入的 XML 不是文件还是一个字符串,我们可以用一下的方式读取
root=ET.fromstring(sample_string)
parse() 方法读取 XML 文件,返回的是 XML 的解析树,类型是 ElementTree
ElementTree 包含以下常用方法:
find(match) # 查找第一个匹配的子元素, match 可以时 tag 或是 xpaht 路径
findall(match) # 返回所有匹配的子元素列表
findtext(match, default=None) # 返回第一项匹配 text 值的元素
iter(tag=None) # 以当前元素为根节点 创建树迭代器,如果 tag 不为 None,则以 tag 进行过滤
iterfind(match) # 通过 tag 和 path 查找所有匹配的子元素列表
fromstring() 方法读取的是字符串,返回解析树的 root 节点,类型是 Element
Element 包含一下常用属性和方法:
attrib # 为包含元素属性的字典
keys() # 返回元素属性名称列表
items() # 返回 (name,value) 列表
get(key, default=None) # 获取属性
set(key, value) # 更新/添加 属性
del xxx.attrib[key] # 删除对应的属性
完整的 API 可以查看以下文档
https://docs.python.org/3/library/xml.etree.elementtree.html?
下面介绍一些常用方法的使用
可以通过简单的循环来遍历每个节点,并可以进行嵌套。
for child in root
print(child.tag,":", child.attrib)
另外,因为有层级关系,所以可以通过下标的方式直接访问各个节点。
print(root[0].tag) #country
print(root[0][0].tag) #rank
下标访问的方法虽然简单,但是在未知 XML 具体结构的时候并不适用,通过 Tag 名称访问的方法更具有普适性。
for neighbor in root.iter('year'):
print(neighbor.text)
Element.findall() 只查找直接的孩子,返回所有符合要求的 Tag 的 Element,而 Element.find() 只返回符合要求的第一个 Element。
前面已经介绍了如何获取一个 Element 的对象,以及查看它的 Tag、Attribute、值和它的孩子。下面介绍如何修改一个 Element 并对 XML 文件进行保存
下面的 Element 请替换成实际相应的变量
Element.set('AttributeName','AttributeValue')
Elemnt.text="new text"
Element.append(childElement)
Element.remove(childElement)
通常比较简单的 Excel 的读写的话,可以用 xlwt 来进行写,xlrd 来进行读。
如果运用 Python 来进行数据分析,一般需要结合 pandas 和 numpy 来进行数据的处理,pandas 也结合了对于 Excel 的处理。
xlwt 和 xlrd 用起来都比较简单,下面的例子是通过 xlrd 和 xlwt 处理的两个实例。
import xlrd
workbook = xlrd.open_workbook(u'sample.xls') //打开Excel文件
sheet_names= workbook.sheet_names() //获取Excel文件中每个Sheet的名字,返回一个list
for sheet_name in sheet_names: //遍历
sheet2 = workbook.sheet_by_name(sheet_name)//通过名字获取sheet
// sheet2 = workbook.sheet_by_index(1) //通过索引获取sheet
print sheet_name rows = sheet2.row_values(3) # 获取第四行内容
cols = sheet2.col_values(1) # 获取第二列内容
print rows
print cols
import xlwt
wbk = xlwt.Workbook()
sheet = wbk.add_sheet('sheet 1') //新建一个Sheet
sheet.write(0,1,'test text')#第0行第一列写入内容
sheet.write_merge(1,1,2,3,"test")//在第2行横向合并第3,4列
sheet.write_merge(2,3,3,3,"test")//在第3,4行纵向合并第4列
wbk.save('test.xls')
另外,在读取 Excel 的时候,会根据字段的类型不同往往会遇到不通的问题。
** 读取内容为日期的格式 **
Python 读取 Excel 时会返回 5 中类型,可以通过 ctype 来查看。这 5 种类型是:
0 empty,1 string,2 number, 3 date,4 boolean,5 error
如果我们不加判断的话可能会遇到读取为空的问题。 可以通过 xldate_as_tuple 来处理为 date 格式。
from datetime import date,datetime
print(sheet1.cell(1,2).ctype)
date_value = xlrd.xldate_as_tuple(sheet1.cell_value(1,2),wb.datemode)
print(date(*date_value[:3]))
print(date(*date_value[:3]).strftime('%Y/%m/%d'))
获取合并的单元格内容
主要会通过 merged_cells(row, row_range, col, col_range) 这个方法来合并单元格,其中 [row, row_range),[col, col_range) 都是左开右闭。
导入 pandas
import pandas as pd
TODO