一个强大的Excel读写库


前言

虽然总是在吹“诶呀c是最好的语言”,“诶呀c就是牛逼”,“python和Java都是c写的,go也是c写的(其实作者也不知道是不是c写的),c++也是基于c的创作”(当然作者也不知道是不是c干的事),但究其原因就是因为作者首先学的c,但学完发现c其实啥也干不来(当然我是说相比于其他语言),心里十分的不平衡

所以today,出一期openpyxl库的使用教程

这个库主要是面向excel表格而设计的,同功能的库还有pandas,有兴趣的小伙伴可以去研究一下一下是二者的大体区别

功能/库 openpyxl pandas
主要用途 精细控制Excel 文件数据分析和批量处理
支持格式 .xlsx .xlsx,.xls,.csv 等
内存效率 中等 高(对于数据分析)
单元格级操作 优秀 有限
公式处理 支持读取和写入 主要读取计算结果
图表操作 支持 不支持
学习曲线 中等 较低(对数据分析师)
依赖关系 纯Python 依赖 NumPy 等科学计算库

安装

现在打开你的vscode使用快捷键**Ctrl+`**来打开终端

然后输入以下命令

1
pip install openpyxl

就完成啦:)

噢噢噢噢,调用的时候不要忘记

1
import openpyxl

基础函数操作

导入表格

如果某一天你打开了一个.xlsx结尾的文件就会发现它有一个文件名称在.xlsx之前,又会发现一大堆sheet如下

head

sheet

我们一般

  • 称表格文件为workbook,也就是wb;
  • 称表格为sheet,也就是ws;
  • 称表格中的单元格为cell,也就是c;
  • 称表格中的行为row,也就是r
  • 称表格中的列为column,也就是col

那么此时,以下几个函数就可以帮你做最excel数据分析最基本的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
wb = workbook("作者真帅") #创建一个workbook,并将其命名为“作者真帅”

wb = openpyxl.load_workbook("文件名.xlsx") #load在英文中的意思是加载,那么我就就很好理解load_workbook的意思了,这是基于你已经存在的一个文件来做修改

ws = wb["sheet1"] #获取名为“sheet1”的sheet

c = ws["A1"] #获取名为“A1”的单元格

r = ws["1"] #获取名为“1”的行

col = ws["A"] #获取名为“A”的列

co = ws.iter_cols(min_row=1, max_row=5, min_col=1, max_col=3) #获取从第1行到第5行,第1列到第3列的所有单元格,返回值是一个迭代器,每个元组是一个列

ro = ws.iter_rows(min_row=1, max_row=5, min_col=1, max_col=3) #获取从第1行到第5行,第1列到第3列的所有单元格,返回值是一个迭代器,每个元组是一个行

print(wb.sheetnames) #获取workbook中的所有sheet

print(ws.max_row) #获取sheet中的最大行数

print(ws.max_column) #获取sheet中的最大列数

print(c.value) #获取单元格的值

print([cell.value for cell in r]) #获取行中所有单元格的值

print([cell.value for cell in col]) #获取列中所有单元格的值

这里做一个解释:以上的所有数据结构都不是正常的所谓列表,字典,或者元组什么的python基础数据结构,而是openpyxl自己定义的一种数据结构,叫做迭代器

1
2
3
4
5
6
7
8
9
迭代器:
迭代器是一个可以记住遍历的位置的对象。
Workbook (wb)
├── Worksheet (ws1)
│ ├── Cell (A1)
│ ├── Cell (B1)
│ └── ...
├── Worksheet (ws2)
└── ...

导出表格

然后是表格的导出

1
wb.save("文件名.xlsx") #保存workbook

进阶操作

刚刚我们学会了表格文件的创建,导入,以及导出,相当于我们学会了c中的printf,scanf(io)
那么接下来我们就要学习openpyxl的进阶用法了

1.激活sheet

可能有的朋友不太理解什么叫做激活sheet,其实就是你打开一个表格文件后,默认显示的那个sheet就叫做活跃的sheet也就是说以下代码可以理解为将目前活跃的sheet赋值给ws

1
ws = wb.active #获取当前激活的sheet

2.创建sheet

一下代码应该就没什么理解困难了,create·动词·创建,remove·动词·删除,后面的0,-1都是索引,如果有朋友问,索引是什么,那我会告诉你,索引就是锁住蚯蚓❤️

1
2
3
4
5
6
7
8
ws1 = wb.create_sheet('Mysheet')     #插入到最后(默认)

ws2 = wb.create_sheet('Mysheet',0) #插入到最前

ws3 = wb.create_sheet('Mysheet',-1) #插入到倒数第二

wb.remove('Mysheet') #删除工作表

3.添加数据

1
2
3
4
5
6
7
8
9
10
11
ws.append([1,2,3]) #在sheet的最后一行添加数据
ws.insert_rows(3,2) #在第3行前面插入2行
ws.insert_cols(3,2) #在第3列前面插入2列

new_row_data = [4, 5, 6, 'JOKER大', 'JOKER小']
for i, value in enumerate(new_row_data):
ws.cell(row=1, column=i+1, value=value)

new_col_data = [7, 8, 9, 10, 'J', 'Q', 'K']
for i, value in enumerate(new_col_data):
ws.cell(row=i+1, column=1, value=value)

来解释一下后面的六行代码,首先new_row_data就是定义一个列表,作为数据(data)后面的函数

enumerate是python的一个内置函数,它的作用是同时列出数据和数据下标,一般用在 for 循环当中,在以以上代码中的意思就是把i作为索引,value作为值,这也就是为什么后面都是i+1,而不是i,因为索引是从0开始,但是openpyxl中数据索引是从1开始的

而后就是cell函数,这个函数的作用是在指定的单元格中添加数据,第一个参数row是行,第二个参数column是列,第三个参数value是值,可以理解为

1
2
3
for i, value in enumerate(new_row_data):
c = [cell.value for cell in r]
c[i] = value

但是我说过,cell什么的是openpyxl自己定义的一种数据结构,所以你不能直接使用c[i] = value来修改单元格的值,而是应该使用cell函数来修改单元格的值,所以以上函数仅供理解,并不可行哦

4.删除数据

1
2
ws.delete_rows(1,2) #删除第1行后两行的数据
ws.delete_cols(4,5) #删除第4列后五列的数据

注释一下,openpyxl的删除数据函数是从第1行开始的,所以如果你想删除第1行到第2行的数据,你需要传入的参数是1,2,而不是0,1,并且openpyxl的删除数据函数是左闭右开的,所以如果你想删除第1行到第2行的数据,你需要传入的参数是1,3,而不是0,2,参数不可以是负数,也不可以是0

5.增删改查代码视觉呈现

1
2
3
4
5
6
7
8
9
10
11
12
13
Traceback (most recent call last):
File "c:\Users\Lenovo\Desktop\VOF.py", line 24, in <module>
wb.save("新建 XLSX 工作表.xlsx") #保存workbook
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\workbook\workbook.py", line 386, in save
save_workbook(self, filename)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\writer\excel.py", line 291, in save_workbook
archive = ZipFile(filename, 'w', ZIP_DEFLATED, allowZip64=True)
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python313\Lib\zipfile\__init__.py", line 1367, in __init__
self.fp = io.open(file, filemode)
~~~~~~~^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '新建 XLSX 工作表.xlsx'

先处理一下以上报错,会出现以上报错是因为权限问题,你可能正打开着文件,所以你需要关闭文件,然后再运行代码

code

show

视图操作

1.合并单元格

1
2
3
4
5
6
7
8
9
10
11
ws.merge_cells('A1:D3') #合并A1到D3的单元格

ws.unmerge_cells('A1:D3') #取消合并A1到D3的单元格

ws.merge_cells(start_column=1, end_column=4,start_row=1, end_row=3) #合并A1到D3的单元格

ws.unmerge_cells(start_column=1, end_column=4,start_row=1, end_row=3) #取消合并A1到D3的单元格

ws.merge_cells# 获取合并的单元格

ws.unmerge_cells# 获取取消合并的单元格

注意,在我们查看合并后的单元格时,只能左上角的那个单元格显示数据,其他的单元格都会显示NONE并且其他单元格都会被清空如:

merge1

merge2

所以我们可以先合并单元格之后再赋值,或者如下这样做

1
2
3
4
5
6
cel = ""
for col in iter_cols(min_row=1, max_row=3, min_col=1, max_col=1):
for c in col:
cel += str(c.value)
ws.merge_cells('A1:D1')
ws['A1'] = cel

展示如下:

merge3

merge4

2.填色

在讲述填色之前先讲一讲计算机显示器是如何显示颜色的,我们都知道三原色是红绿蓝,当然我最爱的是吴亦凡,damn 3押,通过这三种颜色我们可以组合出各种颜色,这也就是rgb颜色计算三个颜色由浅入深的取值范围是0-255,也就是二进制的00000000-11111111

而算起来,每个颜色是八位,那就是二十四位,也不知是为了凑数还是怎么,冯诺依曼的后人开发出了第四个参数透明度:rgba,这就凑出了三十二位的颜色表示(当然openpyxl应该是可以兼容的,但没试过,读者朋友可以去试一试)

以上就是openpyxl的颜色基础,接下来就可以看看openpyxl的颜色函数喽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Color(index=0) # 根据索引进行填充
#
Color(rgb='00000000') # 根据rgb值进行填充
# index
COLOR_INDEX = (
'00000000', '00FFFFFF', '00FF0000', '0000FF00', '000000FF', #0-4
'00FFFF00', '00FF00FF', '0000FFFF', '00000000', '00FFFFFF', #5-9
'00FF0000', '0000FF00', '000000FF', '00FFFF00', '00FF00FF', #10-14
'0000FFFF', '00800000', '00008000', '00000080', '00808000', #15-19
'00800080', '00008080', '00C0C0C0', '00808080', '009999FF', #20-24
'00993366', '00FFFFCC', '00CCFFFF', '00660066', '00FF8080', #25-29
'000066CC', '00CCCCFF', '00000080', '00FF00FF', '00FFFF00', #30-34
'0000FFFF', '00800080', '00800000', '00008080', '000000FF', #35-39
'0000CCFF', '00CCFFFF', '00CCFFCC', '00FFFF99', '0099CCFF', #40-44
'00FF99CC', '00CC99FF', '00FFCC99', '003366FF', '0033CCCC', #45-49
'0099CC00', '00FFCC00', '00FF9900', '00FF6600', '00666699', #50-54
'00969696', '00003366', '00339966', '00003300', '00333300', #55-59
'00993300', '00993366', '00333399', '00333333', #60-63
)
BLACK = COLOR_INDEX[0]
WHITE = COLOR_INDEX[1]
RED = COLOR_INDEX[2]
DARKRED = COLOR_INDEX[8]
BLUE = COLOR_INDEX[4]
DARKBLUE = COLOR_INDEX[12]
GREEN = COLOR_INDEX[3]
DARKGREEN = COLOR_INDEX[9]
YELLOW = COLOR_INDEX[5]
DARKYELLOW = COLOR_INDEX[19]
PURPLE = COLOR_INDEX[6]
PINK = COLOR_INDEX[20]
CYAN = COLOR_INDEX[7]
DARKCYAN = COLOR_INDEX[13]
LIGHTGREY = COLOR_INDEX[21]
DARKGREY = COLOR_INDEX[22]
TURQUOISE = COLOR_INDEX[23]
VIOLET = COLOR_INDEX[24]
LIGHTBLUE = COLOR_INDEX[25]
LIGHTGREEN = COLOR_INDEX[26]
LIGHTYELLOW = COLOR_INDEX[27]
LIGHTPINK = COLOR_INDEX[28]
LIGHTCYAN = COLOR_INDEX[29]

以上是openpyxl的颜色函数源代码,以下是openpyxl的颜色函数的使用

1
2
3
4
5
6
7
8
9
10
11
12
PatternFill(patternType='solid',fgColor=Color(), bgColor=Color())
# fgColor 前景色
# bgColor 后景色
# 参数可选项
patternType = {'darkDown', 'darkUp', 'lightDown', 'darkGrid', 'lightVertical',
'solid', 'gray0625', 'darkHorizontal', 'lightGrid', 'lightTrellis',
'mediumGray', 'gray125', 'darkGray', 'lightGray', 'lightUp',
'lightHorizontal', 'darkTrellis', 'darkVertical'}

ws.cell(3,3).fill = PatternFill()
ws['A1'].fill = PatternFill(fill_type='solid', start_color='FF0000', end_color='FF0000') #填充红色
ws['A1'].fill = PatternFill(fill_type='solid', start_color='00FF00', end_color='00FF00') #填充绿色

3.边框

更上面一样,其实就是几个函数,但是参数比较多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
side = Side(style='thin',color=Color(index=0))
#准确的来说Side只是在设置边框的样式,颜色,宽度,并灭有加上边框
# style可选项
style = ('dashDot','dashDotDot', 'dashed','dotted',
'double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot',
'mediumDashed', 'slantDashDot', 'thick', 'thin')
# 'medium' 中粗
# 'thin' 细
# 'thick' 粗
# 'dashed' 虚线
# 'dotted' 点线
# 'double' 双实线
border = Border(left=side, right=side, top=side, bottom=side)
# 分别设置上下左右的边框,由参数可以看到我们使用了side来传参
for row in ws['A1:B5']:
for cell in row:
# 为每个在A1到A5单元格设置边框
cell.border = border

4.字体

这个没得说,也就是一个函数的调用,但是参数比较烦人

1
2
3
4
5
6
ws.cell(5,3).font = Font(name='仿宋',size=12,color=Color(index=0),b=True,i=True)

# size 字体大小
# b:bold 是否粗体
# i:italic 是否斜体
# name 字体样式

5.对齐

外甥打灯笼————照舅

1
2
3
4
5
6
7
8
9
Alignment(horizontal='fill',vertical='center')

# 参数可选项
horizontal = {'fill', 'distributed', 'centerContinuous', 'right',
'justify', 'center', 'left', 'general'}

vertical = {'distributed', 'justify', 'center', 'bottom', 'top'}

ws.cell(3,3).alignment= Alignment()

解释一下上述参数(因为太多了,所以不在代码段中解释):

  • horizontal:水平
    • fill:填充
    • distributed:分散
    • centerContinuous:连续居中
    • right:右对齐
    • justify:两端对齐
    • center:居中
    • left:左对齐
  • vertical:垂直
    • general:常规
    • distributed:分散
    • justify:两端对齐
    • center:居中
    • bottom:底部
    • top:顶部

6.宽高

最后一个问题,两个函数解决

1
2
ws.row_dimensions[1].height = 100 #设置行高
ws.column_dimensions['A'].width = 100 #设置列宽

上述dimensions是ws对象的一个属性,row_dimensions是行的属性,column_dimensions是列的属性,height是行的高度,width是列的宽度

🔥🔥🔥完结撒花🔥🔥🔥

骗你的

你以为这就完了?

开玩笑呢?

中国改革开放和现代化建设的总设计师邓小平曾说过:实践是检验真理的唯一标准

那么,此时,是时候来一场酣畅淋漓的项目演习辣

go go go! 出发喽

项目背景

你是一个超级喜欢🦅语课的大学生,但你的🦅语老师cray女士最近却因为一件事头疼:

每学期的期末xx通都会通过n次线上作文测试和期中考试,期末成绩来生成n+2个表格,cray老师需要提取出这些表格中的数据,然后进行平均分的计算以及总成绩的计算,然后再将这些数据导入到一个新的表格中,但是cray老师使用excel并不熟练

喜欢🦅语的你可以写一个小项目来帮助cray老师吗

项目要求

你需要完成以下功能:

1.写一个函数可以读入表格并且将表格中的所有行通过学号排序

2.写一个函数可以提取出学号列,姓名列以及每份文件的成绩列添加入新的表格中

3.写一个函数可以计算出每人作文平均为并且加入到新表格中

4.写一个函数可以计算出总分

项目规划

当然这是作者的习惯,在项目开始之前做一个简介的流程图,推荐网站(个人更喜欢后者):

思维导图如下:

flow chart

项目代码

  • 功能一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from openpyxl import *
def average(ws,data,per,row):
return ws.cell(row=row,column=(ws.max_column+1),value=round(float(data/per), 2))
# 平均值函数

def add(ws,row,min_col,max_col):
result = 0.00
for col in ws.iter_cols(row, row, min_col, max_col):
for c in col:
if c.value is None:
c.value = 0.00
result += c.value
return ws.cell(row=row, column=(ws.max_column + 1), value=round(float(result), 2))
# 求和函数
  • 功能二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from openpyxl import *
def change(ws):
for row in ws.rows:
for c in row:
if c.value=='学号':
col=c.column
row=c.row
break
if row and col:
break

school_number=[]
for r in ws.iter_rows(min_row=row+1,min_col=col,max_col=col,max_row=ws.max_row):
for c in r:
school_number.append((c.value,c.row))

sort_data=sorted(school_number, key=lambda x: x[0])
sort_row=[item[1] for item in sort_data]
new_row = []
for row_num in sort_row:
new_row.append([cell.value for cell in ws[row_num]])

for row in ws.iter_rows(min_row=row+1, max_row=ws.max_row):
for cell in row:
cell.value=None

for index,row_data in enumerate(new_row, start=row+1):
for col_index, value in enumerate(row_data, start=1):
ws.cell(row=index, column=col_index, value=value)

return ws
def get_work(path):
wb=load_workbook(path)
ws=wb.active
ws=change(ws)
col=ws.max_column
return wb,col,ws
def get_name(path):
wb,col,ws=get_work(path)
name=[]
for row in ws.rows:
for c in row:
if c.value=='姓名':
col=c.column
row=c.row
break
if row and col:
break
for r in ws.iter_rows(min_row=row+1, min_col=col, max_col=col, max_row=ws.max_row):
for c in r:
name.append(c.value)
return name
def get_number(path):
wb,col,ws=get_work(path)
number=[]
for row in ws.rows:
for c in row:
if c.value=='学号':
col=c.column
row=c.row
break
if row and col:
break
for r in ws.iter_rows(min_row=row+1, min_col=col, max_col=col, max_row=ws.max_row):
for c in r:
number.append(c.value)
return number
def get_score(path):
wb,col,ws=get_work(path)
score=[]
for row in ws.rows:
for c in row:
if c.value=='总成绩':
col=c.column
row=c.row
break
if row and col:
break
for r in ws.iter_rows(min_row=row+1, min_col=col, max_col=col, max_row=ws.max_row):
for c in r:
score.append(c.value)
return score
  • 功能三
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from openpyxl import *
from openpyxl.styles import Alignment
from function_one import average, add
from function_two import get_name, get_number, get_score
def new_work():
wb=Workbook()
ws=wb.active
ws.merge_cells('A1:F1')
ws['A1']='平均成绩表'
ws['A1'].alignment = Alignment(horizontal='center', vertical='center')
ws.append(['姓名','学号','作文平均分','期末听力','期末成绩','总分'])
return wb,ws
def write_data(ws, name, number, score):
for i in range(len(name)):
ws.append([name[i], number[i], None, score[i]])
return ws
def write_average(ws,score):
per = len(score)
for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
for cell in row:
if cell.value == '作文平均分':
col = cell.column
row = cell.row
break
if row and col:
break
for i in range(len(score[1])):
data = 0
for j in range(len(score)):
data += score[j][i]
average(ws, data, per, row)
return ws
def write_sum(ws):
for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
for cell in row:
if cell.value == '总分':
col_all = cell.column
row_all = cell.row
break
if cell.value == '作文平均分':
col_wri = cell.column
row_wri = cell.row
break
if row_wri and col_wri and row_all and col_all:
break
for i in range(row_all+1, ws.max_row + 1):
add(ws, i, col_wri, col_all)
return ws
def main(path1,path2,path3,path4,path5):
print("请cray老师把四次作文成绩放到前四个,把听力成绩放到第五个")
name=[]
name=get_name(path1)
number=[]
number=get_number(path1)
score=[get_score(path1),get_score(path2),get_score(path3),get_score(path4)]
wb,ws=new_work()
liston=[]
liston=get_score(path5)
ws=write_data(ws, name, number, liston)
ws=write_average(ws, score)
ws=write_sum(ws)
wb.save("成绩汇总.xlsx")

以上三个代码实现了全部目的,作者特意用了网易有道词典让代码的可读性更高

上述代码中作者手动添加了几个错误,而你,我的朋友,如果能在没有ai的辅助下理解代码并且成功运行,我将献上最崇高的敬意,赞美愚者(诡秘之主真的好看,推荐推荐)

写在最后

本篇博客既是教程,也是笔记,后续会接着发《浅入理解C语言》,《python基础无痛手术——放心,真的不难》,《汇编tiny笔记》,《二进制漏洞与挖掘——Linux pwn 栈》,《二进制漏洞与挖掘——Linux pwn 堆》,《算法的一袋米要扛到几楼》,《一个强大的图像处理库》,《数据结构代码实现》等

这么一看我还学了挺多东西呢,哈哈

🔥🔥🔥感谢观看,这回真完结撒花了🔥🔥🔥