什么场景需要它#
每个学期都会有这样的表格工作: 把几十份学生成绩表汇总成一张总表、按班级拆分成多个文件、 批量计算总分和排名。手工做一次要半小时,还容易出错。
这种结构固定、重复度高的工作,正是代码最擅长的。
安装#
pip install openpyxl
一、读取表格#
from openpyxl import load_workbook
# data_only=True 表示读取公式的计算结果,而不是公式本身
wb = load_workbook("成绩表.xlsx", data_only=True)
# 按名称获取工作表
ws = wb["Sheet1"]
# 用 iter_rows 逐行读取,values_only=True 直接拿到值而不是单元格对象
for row in ws.iter_rows(min_row=2, values_only=True): # 跳过标题行
name, chinese, math, english = row[0], row[1], row[2], row[3]
print(name, chinese, math, english)
小技巧:
ws.max_row和ws.max_column能拿到表格的行列数, 但注意如果表格中间有空行,这两个值可能不准。
二、写入表格#
from openpyxl import Workbook
wb = Workbook() # 新建工作簿
ws = wb.active
ws.title = "成绩汇总" # 重命名默认工作表
# 写标题行:append 会自动往后找空行
ws.append(["姓名", "语文", "数学", "英语", "总分"])
# 写数据行,顺便算总分
students = [("张三", 88, 92, 85), ("李四", 76, 95, 80)]
for name, c, m, e in students:
ws.append([name, c, m, e, c + m + e])
wb.save("汇总结果.xlsx")
print("已生成 汇总结果.xlsx")
三、加一点格式#
数据能看懂的前提是排版清晰。
from openpyxl.styles import Font, Alignment, PatternFill
from openpyxl.utils import get_column_letter
# 标题行加粗、加底色、居中
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill("solid", fgColor="0085A1")
for cell in ws[1]: # ws[1] 就是第一行
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal="center")
# 自动调整列宽
# get_column_letter(1) 把数字 1 转成字母 "A"
for i in range(1, ws.max_column + 1):
letter = get_column_letter(i)
ws.column_dimensions[letter].width = 12
四、实用的完整例子:按班级拆分表格#
from openpyxl import load_workbook, Workbook
from collections import defaultdict
wb = load_workbook("全年级成绩.xlsx", data_only=True)
ws = wb.active
# 按班级分组:defaultdict 的好处是不用先判断 key 存在不存在
groups = defaultdict(list)
# 假设第一列是班级,第二列是姓名,后面是各科分数
for row in ws.iter_rows(min_row=2, values_only=True):
if row[0] is None:
continue # 跳过空行
class_name = str(row[0]).strip()
groups[class_name].append(row[1:])
# 每个班级写一个文件
for class_name, rows in groups.items():
new_wb = Workbook()
new_ws = new_wb.active
new_ws.title = class_name
new_ws.append(["姓名", "语文", "数学", "英语"])
for r in rows:
new_ws.append(r)
new_wb.save(f"{class_name}_成绩.xlsx")
print(f"{class_name}: {len(rows)} 人")
这段代码把「手工复制粘贴几十次」压缩成了 20 行。
常见报错#
| 报错 | 原因 | 解决 |
|---|---|---|
FileNotFoundError |
文件路径不对 | 检查文件名,注意扩展名是 .xlsx 不是 .xls |
KeyError: 'Sheet1' |
工作表名不对 | 用 wb.sheetnames 看真实的工作表名 |
PermissionError |
文件正被 Excel 打开 | 先关掉 Excel 再运行 |
注意:
openpyxl不支持老式的.xls格式。 遇到.xls要先在 Excel 里另存为.xlsx。
课堂思考题#
- 为什么读取时要用
data_only=True?不加会有什么后果? - 如果表格有几万行,一次性读进内存会有问题吗?有没有更省内存的写法?