简介
openpyxl 是一个 Python 库,用于读取/写入 Excel 2010 xlsx/xlsm/xltx/xltm 文件,并不支持xls文件
一、安装openpyxl模块
pip install openpyxl
二、xlsx文件读取数据方法
openpyxl库在调用部分方法时,没有出现提示,所以使用时,可以按照以下方法导入对应方法名
from openpyxl import load_workbook
from openpyxl.workbook.workbook import Workbook, Worksheet
from openpyxl.cell.cell import Cell
读取数据步骤如下:
1.加载工作簿,一个excel文件workbook
2.读取工作表sheet
3.确定单元格并输出内容
1.加载工作簿(workbook)
一个Excel文件就是一个工作簿,是一个openpyxl.workbook.workbook.Workbook的对象(可以通过打印一个workbook数据类型看出来),这是一个可迭代对象,可以进行for循环来获取工作表(sheet)
wb = load_workbook('cases.xlsx')
print(wb, type(wb))
2.读取工作表(sheet)
工作表是在工作簿中的,一个工作簿中可以有多个工作表,很显然,要获取工作表肯定是先获取工作簿,上面说到workbook是一个可迭代对象,下面验证一下。
wb = load_workbook('cases.xlsx')
print(iter(wb))
print(iter([1, 3, 'a']))
print(iter('abc'))
输出:
上述代码发现workbook是一个与列表一模一样的列表迭代器,所以workbook也是可以利用列表取值的方式进行取值。
2.1 用for循环遍历工作簿中的所有工作表
既然workbook是一个列表迭代器,那就可以用 for 循环:
for i in wb:
print(i)
2.2 列表取值的方式读取工作表
ws = wb['login']
print(iter(ws))
for i in ws:
print(i)
print(ws['A2'])
输出:
(
(
(
通过上述代码发现,工作表sheet也是一个可迭代对象,通过for循环可以获取到每个单元格,以每一行作为一个元组
2.3获取当前工作表
当前工作表的意思启动Excel文件时,打开的第一个工作表,比如:初始默认是打开sheet1,之后再sheet2中编辑内容保存后,关闭Excel文件再次启动时,会默认打开sheet2而不是sheet1,此时wb.active就是sheet2了
ws=wb.active
2.4获取工作表的最大行列数
max_r = ws.max_row #最大列
max_c = ws.max_column #最大列
3.读取单元格
每个工作表表都有各自的单元格,所以要读取指定的内容,先要指定工作表
3.1指定行数和列数读取单元格
cell = ws.cell(row=2, column=1) #读取单元格名称:A1、B3...
cell = ws.cell(row=2, column=1).value #读取单元格中的内容
注意问题:
1、通过cell对象的value属性可以获取值
2、value只有两种数据类型:字符串,数值(数值以外的就是字符串)
3、如果cell里没有值,返回None
3.2用for循环读取单元格
上述2.2中的代码有说明,通过for循环可以获取到每个单元格,每一行单元格中的数据组成一个元组
for i in ws:
print(i)
3.3通过单元格编号读取
c = ws['A4']
4.把工作表中数据构造成字典、列表
需求:把表格中的每一行数据都构造成字典,之后组成一个列表,需要实现如下效果
[{'class': 1, 'name': '张一', 'student_id': 11, 'grade': {'语文': 85, '数学': 95, '英语': 85, '历史': 80}}, {'class': 1, 'name': '张二', 'student_id': 12, 'grade': {'语文': 78, '数学': 65, '英语': 90, '历史': 70}}, {'class': 1, 'name': '张三', 'student_id': 13, 'grade': {'语文': 77, '数学': 64, '英语': 89, '历史': 69}}, {'class': 2, 'name': '李一', 'student_id': 14, 'grade': {'语文': 76, '数学': 63, '英语': 88, '历史': 68}}, {'class': 2, 'name': '李二', 'student_id': 15, 'grade': {'语文': 75, '数学': 62, '英语': 87, '历史': 67}}, {'class': 2, 'name': '李三', 'student_id': 16, 'grade': {'语文': 74, '数学': 61, '英语': 86, '历史': 66}}]
实现方法一:
def get_data_from_excel(filename, sheet_name=None):
"""
从Excel中读取数据,构造数据
:param filename: Excel工作簿文件名称,需包含路径
:param sheet_name: 工作表
:return:
"""
# 1.加载工作簿
wb = load_workbook(filename)
# 2.读取工作簿
if sheet_name is None:
ws = wb.active
else:
ws = wb[sheet_name]
# print(ws)
# 3.获取最大的行数和列数
max_r = ws.max_row
max_c = ws.max_column
# print(max_r, max_c)
# 创建空列表,到时候直接把每行数据追加进去即可
data = []
# 获取工作表中的第一行数据,作为标题
title = {}
for i in range(1, max_c+1):
# print(1, i)
# print(ws.cell(row=1, column=i).value)
title[i] = ws.cell(row=1, column=i).value
# print(title)
# title={1: 'title', 2: 'username', 3: 'password', 4: 'expect'}
# 获取值
for x in range(2, max_r+1):
# 临时变量,表示一行数据
temp = {}
for y in range(1, max_c+1):
# print(x, y, end=' ')
# print(title[y], ws.cell(row=x, column=y).value, end=' ')
temp[title[y]] = ws.cell(row=x, column=y).value
# 最后一列数据是字符串,要转换成字典
temp[title[y]] = eval(ws.cell(row=x, column=y).value)
# print(temp[title[y]], type(temp[title[y]]))
# print(temp)
data.append(temp)
return data
if __name__ == '__main__':
res=get_data_from_excel('cases.xlsx', sheet_name='login')
print(res[0]['expect'], type(res[0]['expect']))
实现方法二:
from openpyxl import load_workbook
def get_temp_from_excel(file_name, sheet_name=None):
"""
:param file_name:
:param sheet_name:
:return:
"""
# 加载工作簿
wb = load_workbook(file_name)
# 读取工作表,如果不传工作表,就默认为当前工作表
if sheet_name is None:
ws = wb.active
else:
ws = wb[sheet_name]
# 工作表最大行数和列数
max_r = ws.max_row
max_c = ws.max_column
# 接收最终数据
data = []
# 临时变量
temp = []
# 获取sheet中的内容
for i in ws: # 只能获取到单元格的编号,每行输出一个元组
# print(i, type(i))
i = list(i) # 把元组转换成列表
# print(i)
for j in range(0, max_c): # 获取每个单元格中的值
i[j] = i[j].value
# print(i)
# print(i[j], type(i[j]))
temp.append(i) # 把单元格中的值,加入到临时列表中,以每行一个列表的形式
# print(temp)
# temp中的第一个元素就是要作为字典的key,其他行的数据作为value
for x in range(1, max_r):
# print(x)
dic = dict(zip(temp[0], temp[x])) # 把temp中第二行开始的数据构造成字典
dic['grade'] = eval(dic['grade']) # 把分数这列数据从字符串转换成字典
# print(dic)
data.append(dic)
# print(temp)
# print(data)
return data
if __name__ == '__main__':
res = get_temp_from_excel('cases.xlsx', sheet_name='Sheet2')
print(res)
上述两个方法使用了两个内置函数
eval()函数
eval() 函数用来执行一个字符串表达式,并返回表达式的值。
eval(expression[, globals[, locals]])
参数
expression -- 字符串表达式。
globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
返回值:返回表达式计算结果。
print(eval('2+3'))
返回字符串表达式2+3的值,上述代码会输出5
字符串与list、tuple、dict的转化
a = '[1, 2, 3]'
b = '(4, 5, 6)'
c = '{"语文": 85, "数学": 95, "英语": 85, "历史": 80 }'
ra = eval(a)
rb = eval(b)
rc = eval(c)
print(ra, type(ra))
print(rb, type(rb))
print(rc, type(rc))
输出:
[1, 2, 3]
(4, 5, 6)
{'语文': 85, '数学': 95, '英语': 85, '历史': 80}
zip()函数
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。
可以使用 list() 转换来输出列表,当有且只有两个可迭代对象传入时,可以使用dict来输出字典。
zip()函数的定义:从参数中的多个迭代器取元素组合成一个新的迭代器;
返回: 返回一个zip对象,其内部元素为元组;可以转化为列表或元组;
传入参数: 元组、列表、字典等迭代器。
当zip()函数中只有一个参数时,zip(iterable)从iterable中依次取一个元组,组成一个元组。
各个迭代器的元素个数一致
ls1 = ['a', 'b', 'c']
ls2 = ['aa', 'bb', 'cc']
ls3 = ['aaa', 'bbb', 'ccc']
ls=list(zip(ls1, ls2, ls3))
dt=dict(zip(ls1, ls2))
print(ls)
print(dt)
输出
ls:[('a', 'aa', 'aaa'), ('b', 'bb', 'bbb'), ('c', 'cc', 'ccc')]
dt:{'a': 'aa', 'b': 'bb', 'c': 'cc'}
各个迭代器的元素个数不一致
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同
ls1 = ['a', 'b', 'c']
ls2 = ['aa', 'bb']
ls3 = ['aaa', 'bbb', 'ccc']
ls=list(zip(ls1, ls2, ls3))
dt=dict(zip(ls1, ls2))
print(ls)
print(dt)
输出:
[('a', 'aa', 'aaa'), ('b', 'bb', 'bbb')]
{'a': 'aa', 'b': 'bb'}
代码与Excel中测试用例的改进
方法一和方法二都存在明显的缺陷:
缺陷1:单元格数据格式的缺陷
单元格中的值只能是字符串或者数值,如果值为空就是None,在设计测试用例时,经常会遇到某个值为空的情况(如:登录功能,会设计密码为空和用户名为空的用例),当在Excel单元格中输入数字,就会返回数值,但是在实际测试中,可能需要的是字符串格式,而不是数值,此时,必须再次转换,非常麻烦。
缺陷2:eval()函数自身的缺陷会造成安全问题
尽量不使用eval()函数
基于上述两个缺陷,需要改进代码,把测试数据和期望数据都各自使用一个字典来撰写,以注册功能为例
上述表格中期望数据已经是使用字典格式,再把测试数据改成同样格式
此时request_data和expect_data在Excel中都是一个字典,但是从Python中读取的时候就是一个字符串(单引号中的内容是字典)了,如:'{"code": 1, "msg": "注册成功"}',实际上这又是一种新的数据格式,叫做json字符串。
json字符串有两个方法loads和dumps,可以实现json字符串和Python对象相互转换
json的简单使用
json.loads把json字符串转成Python对象
json.dumps把Python对象转换成json字符串
import json
dt = '{"username": "tonych", "password1": "123456", "password2": "123456"}'
# json.loads把json字符串转成Python对象
st = json.loads(dt)
print(st, type(st))
# json.dumps把Python对象转换成json字符串
cc = json.dumps(st)
print(cc, type(cc))
print(json.dumps(1), type(json.dumps(1)))
经常会忘记这两个方法哪个是从json字符串转成Python对象,哪个是从Python的对象转换成json字符串
在使用者两个方法时,可以看提示
使用json.loads时,可以看到括号中提示的第一个参是S,表示要输入的是字符串,所以json.loads是把json字符串转成Python对象
使用json.dumps时,可以看到括号中提示的第一个参是obj,表示要输入的是Python对象,所以json.dumps是把Python对象转成json字符串
代码的改动不大,就是把之前代码中的eval()函数这行代码去掉即可,在测试用例中的使用Excel的期望数据和测试数据时,就使用json.loads()方法,把json字符串转换成字典,这样就方便使用了,可以避免Excel单元格中的数据只能是数值或字符串或者是None的问题了
from openpyxl import load_workbook
def get_data_from_excel(file_name, sheet_name=None):
# 1.加载wordbook
wb = load_workbook(file_name)
# 2.读取sheet
if sheet_name is None:
ws = wb.active
else:
ws = wb[sheet_name]
# 3.获取sheet最大行列
max_r = ws.max_row
max_c = ws.max_column
# print(max_c, max_r)
# 4.获取单元格内容
data = []
# 4.1获取第一行内容作为字典标题
title = {}
for i in range(1, max_c+1):
title[i] = ws.cell(row=1, column=i).value
# print(title)
# 4.2 获取值
for j in range(2, max_r+1):
# 每行数据一个字典
temp = {}
for i in range(1, max_c+1):
temp[title[i]] = ws.cell(row=j, column=i).value
data.append(temp)
return data
if __name__ == '__main__':
res = get_data_from_excel('test_register_case.xlsx')
print(res)