openpyxl模块的简单使用方法

简介

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.把工作表中数据构造成字典、列表

image.png

需求:把表格中的每一行数据都构造成字典,之后组成一个列表,需要实现如下效果

[{'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()函数
基于上述两个缺陷,需要改进代码,把测试数据和期望数据都各自使用一个字典来撰写,以注册功能为例

image.png

上述表格中期望数据已经是使用字典格式,再把测试数据改成同样格式
image.png

此时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对象

image.png

使用json.dumps时,可以看到括号中提示的第一个参是obj,表示要输入的是Python对象,所以json.dumps是把Python对象转成json字符串
image.png

代码的改动不大,就是把之前代码中的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)

你可能感兴趣的:(openpyxl模块的简单使用方法)