Python 入门(四)- Openpyxl 操作 Excel 教程

1. 简介

Openpyxl 官方教程:openpyxl.readthedocs.io/en/stable/t...

Excel 官网:support.microsoft.com/zh-cn/excel

以往我们处理 Excel 文件,只能手动打开 Excel 文件进行操作。如果碰到大量且重复性高的任务,一个个编辑文件就特别费时费力。这时我们可以借助 Python 读取和写入 Excel 文件的库 来进行文件的批量以及自动化处理。Python 提供了好几个能够操作 Excel 的库。本篇文章选择 Openpyxl 库作为教程。

考虑到有的朋友可能对 Excel 不太熟悉,我先在开头简单回顾一下 Excel 的基础概念

  • 层级关系

    erlang 复制代码
    工作簿(Workbook)
    ├── 工作表(Worksheet)
    │ ├── 单元格(Cell)
    │ ├── 单元格(Cell)
    │ └── ...
    ├── 工作表(Worksheet)
    └── ...

    一个工作簿包含多个工作表,每个工作表又由无数个单元格组成,单元格是 Excel 最小的数据存储单位

  • 工作簿‌(Workbook)

    工作簿是 Excel 官方的叫法,它其实就是一个 Excel 文件,通常以 .xlsx 或 .xls 为扩展名

    文件扩展名:

    yaml 复制代码
    .xls ------ 普通 Excel 工作簿(Excel 早期版本:1997--2003)
    
    .xlsx ------ 普通 Excel 工作簿‌(Excel 2007 及之后版本)
    
    .xlsm ------ 带宏的工作簿
    
    .xlsb ------ 二进制的工作簿

    创建工作簿的时候会自动生成 ‌1 个工作表‌

  • 工作表‌(Worksheet)

    工作表是工作簿内部的一张张独立表格。工作表的名称‌默认为"Sheet1"、"Sheet2"、"Sheet3" 依次递增,也可自行修改

    工作表用于分类存放数据。例如一个名为"2026年财务报表"的工作簿,可以包含"1月"、"2月"等多个工作表

    每个工作表是由行(Rows:从 1 开始编号,如 1, 2, 3...)和列(Columns:用字母表示A, B, C...)组成

  • 单元格(Cell)

    单元格就是工作表上的一个个小格子。它是存储数据的最小单位,用来输入文本(字符串),数值(数字),日期 / 时间,公式(以 = 开头)

    每个单元格都有唯一的地址标识,由「列标(字母) + 行号(数字)」组成。例如 A 列和第 1 行交叉的单元格,地址为 A1;C 列和第 5 行交叉的单元格,地址为 C5

    当你点击某个格子,它周围会出现粗黑边框,此时它就是"活动单元格",你可以直接输入数据

Excel 工作界面:

PS:Openpyxl 的官方文档真是乱糟糟的,连一个目录总览都没有,全靠上一页下一页的挨个翻阅,翻了半天也看不到头,简直无力吐槽

PS:切换全屏:Ctrl+Shift+F1,再次按取消全屏

2. 安装

我使用 uv 作为 python 包管理工具。为了节省文章篇幅,安装 uv、使用 uv 安装 python 以及创建项目、创建虚拟环境等步骤略过。不会 uv 的同学可以看我的另一篇文章:

📝 《Python 入门(一)- 用 UV 管理 Python》:juejin.cn/post/760585...

安装 openpyxl 库

bash 复制代码
uv add openpyxl

安装后会在 pyproject.toml 文件写入 openpyxl 依赖

以及在 uv.lock 文件锁定 openpyxl 包的版本

3. 工作簿(Excel 文件)

3.1 工作簿创建

Workbook() 实例化的时候会自动创建一个工作表,并自动生成默认表名"Sheet"

Workbook.save() 方法将工作簿保存到磁盘上的文件中。如果文件不存在将创建文件,如果文件存在则进行内容覆盖

注意!覆盖 Excel 内容的时候,需要将当前打开的 Excel 文件关闭才能执行代码,否则 Excel 文件被占用,执行 Workbook.save() 方法会报错

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象
print("wb:", wb)


wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

成功创建工作簿,并默认创建了一个名为 "Sheet" 的工作表

也可以指定路径创建,我这里将创建的 excel 文件放在 D 盘的 xlsx 文件夹下面

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象
print("wb:", wb)


wb.save("D:\\xlsx\\sample.xlsx")  # 保存 excel
print("创建成功!")

注意!路径(xlsx 文件夹)需要存在,否则会报错

3.2 工作簿加载

注意,Openpyxl 不兼容旧版本的 Excel(Office2003 的 xls 格式)!旧版本可以用 xlrd 库进行 Excel 文件的读取,并且 xlrd 库要指定 1.2.0 版本才能同时支持新旧 Excel 版本;或者将旧版本的 xsl 转换为 xlsx 格式(推荐)

我不想为了兼容旧版本的 Excel 去多学一个库,所以我还是选择用 Openpyxl 库加载 Excel 文件

使用 load_workbook() 函数打开现有的 Excel 文件

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")

# 获取所有工作表名称
print(wb.sheetnames)

打印出工作表的名称证明加载成功

3.3 工作簿删除

使用 os 库的 remove() 方法删除工作簿

python 复制代码
import os

os.remove("sample.xlsx")

print("工作簿删除成功!")

也可以使用 os 库的 path.exists() 方法加一个判断提升代码健壮性:如果文件存在才删除,否则提示不存在

python 复制代码
import os

# 删除工作簿文件
file_path = "sample.xlsx"
if os.path.exists(file_path):
    os.remove(file_path)
    print(f"已删除文件: {file_path}")
else:
    print("文件不存在")

3.4 合并工作薄

我们可以将多个结构相同的 Excel 工作薄合并为一个

具体操作:略

3.5 拆分工作薄

我们也可以将结构相同的 Excel 工作薄拆分为多个

具体操作:略

4. 工作表

4.1 修改工作表的名称

Workbook() 实例化的时候会自动创建一个工作表,并自动生成默认表名。可以使用 Workbook.title 属性更改工作表的名称

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象

ws = wb.active  # 获取当前活跃的工作表
ws.title = "工资表"  # 修改当前工作表的名称

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

表名被成功修改为"工资表"

4.2 创建新的工作表

create_sheet() 函数共有两个参数:

title(表名):若不指定工作表的名称,将按顺序递增自动生成(Sheet、Sheet1、Sheet2、...);如果表名重复了,它会自动加上数字1,2,3依此类推

index(索引值):正数从 0 开始从前往后生成工作表,负数从 -1 开始从后往前生成工作表,索引不指定时追加到最右侧

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象

ws2 = wb.create_sheet("天", 0)  # 正数索引:从0开始,指定插入位置
ws2 = wb.create_sheet("青", 1)
ws2 = wb.create_sheet("色", 2)
ws2 = wb.create_sheet("等", 3)
ws2 = wb.create_sheet("烟", 4)
ws2 = wb.create_sheet("等", -1)  # 负数索引:从末尾开始计数
ws2 = wb.create_sheet("在", -2)
ws2 = wb.create_sheet("我", -3)
ws2 = wb.create_sheet("而", -4)
ws2 = wb.create_sheet("雨", -5)

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

我们按照歌词 "天青色等烟雨而我在等" 的顺序创建工作表

表名重复了,有两个,所以第二个被自动加上了数字1

默认创建的 Sheet 因为没加索引,所以默认排在最后

现在我们加上一行 ws1 = wb.create_sheet("你") 代码,索引为空将表插入到末尾

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象

ws1 = wb.create_sheet("你")  # 索引为空:插入到末尾
ws2 = wb.create_sheet("天", 0)  # 正数索引:从0开始,指定插入位置
ws2 = wb.create_sheet("青", 1)
ws2 = wb.create_sheet("色", 2)
ws2 = wb.create_sheet("等", 3)
ws2 = wb.create_sheet("烟", 4)
ws2 = wb.create_sheet("等", -1)  # 负数索引:从末尾开始计数
ws2 = wb.create_sheet("在", -2)
ws2 = wb.create_sheet("我", -3)
ws2 = wb.create_sheet("而", -4)
ws2 = wb.create_sheet("雨", -5)

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

注意看!神奇的事情发生了

表名 ,索引为空所以插入到末尾,这个很好理解

但是默认创建的 Sheet 跑到了中间,这又是咋回事?

我的理解是插入到末尾已经被表名 给占用了,所以默认创建的 Sheet 只能放在不属于这些带有索引下标的位置-也就是中间

所以大家创建多个表的时候,建议每个表都指定下标索引,避免排序混乱

4.3 查看所有工作表的名称

Workbook.sheetname() 函数查看工作簿中所有工作表的名称

在前面的 [3.2 工作簿加载](#3.2 工作簿加载 "#32-%E5%B7%A5%E4%BD%9C%E7%B0%BF%E5%8A%A0%E8%BD%BD") 章节已经用过此方法了,不再赘述

4.4 移动工作表(下标)

move_sheet(title, n) 函数移动工作表的下标,对其进行重新排序,参数如下:

title:Sheet 名称

n:负数表示向左移动,正数表示向右移动,数字表示移动几个位置

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")

print(wb.sheetnames)  # 获取所有工作表名称

wb.move_sheet("Sheet", 1)  # -1 负数表示向左移动,正数表示向右移动,数字表示移动几个位置

print(wb.sheetnames)  # 获取所有工作表名称

加载刚才创建的工作簿,然后将默认创建的 Sheet 向右移动了一个位置

需要保存 excel 的话,在最后调用 wb.save("sample.xlsx") 函数即可

4.5 复制工作表

Workbook.copy_worksheet(sheet):在当前工作簿复制指定的工作表并返回复制后的工作表对象

python 复制代码
from openpyxl import Workbook

wb = Workbook() # 实例化 excel 工作簿对象

ws = wb.active  # 获取当前活跃的工作表
ws.title = "工资表"  # 修改当前工作表的名称

new_worksheet = wb.copy_worksheet(ws)  # 复制工作表

wb.save("sample.xlsx") # 保存 excel
print("创建成功!")

不指定复制指定的工作表的名字的话,默认会在表名后加上 Copy

如果想指定名字的话加上 new_worksheet.title = "复制的工资表" 这句代码即可

4.6 删除工作表

del wb['SheetName']:在当前工作簿中删除指定的工作表

wb 是已加载的工作簿对象

SheetName 是想要删除的表名

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")

print(wb.sheetnames)  # 获取所有工作表名称

del wb["工资表 Copy"]  # 删除工作表

print(wb.sheetnames)  # 获取所有工作表名称

加载刚才创建的工作簿,然后将名为 "工资表 Copy" 的工作表删除

需要保存 excel 的话,在最后调用 wb.save("sample.xlsx") 函数即可

注意:旧的 remove_sheet() 删除工作表方法已被废弃

5. 单元格

5.1 单元格的内容写入

5.1.1 通过工作表的键

通过工作表的键填写单元格内容

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象

ws = wb.active  # 获取当前活跃的工作表
ws.title = "工资表"  # 修改当前工作表的名称
ws["A1"] = "姓名"  # 给单元格填写值(通过工作表的键)

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

也可以加上 .value

python 复制代码
# 给单元格填写值(通过工作表的键)
ws["A1"].value = "工资表"

通过工作表的键获取单元格内容

python 复制代码
print(ws["A1"].value)

5.1.2 通过行列表示法

通过行号列号进行填写,注意是从1开始

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象

ws = wb.active  # 获取当前活跃的工作表
ws.title = "工资表"  # 修改当前工作表的名称
# 给单元格填写值(通过行列表示法):row 行,column 列,value 值
ws.cell(row=1, column=1, value="姓名")

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

也可以这样表示 ws.cell(row=1, column=1).value = "姓名"

python 复制代码
from openpyxl import Workbook

wb = Workbook()  # 实例化 excel 工作簿对象

ws = wb.active  # 获取当前活跃的工作表
ws.title = "工资表"  # 修改当前工作表的名称
# 给单元格填写值(通过行列表示法):row 行,column 列,value 值
ws.cell(row=1, column=1).value = "姓名"

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

通过行号列号获取单元格内容

python 复制代码
print(ws.cell(row=1, column=1).value)

5.1.3 通过 append 批量填写

python 复制代码
from openpyxl import Workbook

wb = Workbook()

ws = wb.active
ws.title = "人员表"  # 修改当前工作表的名称

treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]
for row in treeData:
    ws.append(row)

wb.save("sample.xlsx")
print("创建成功!")

批量填写更加简单快捷,代码也容易理解

5.1.4 查看所有单元格的值

可以在保存前先打印出单元格的值,如果符合要求再进行保存

python 复制代码
from openpyxl import Workbook

wb = Workbook()

ws = wb.active
ws.title = "人员表"  # 修改当前工作表的名称

treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]
for row in treeData:
    ws.append(row)


# 遍历工作表中的所有行中的每一个单元格
print(
    "\n".join(["\t".join([str(cell.value) for cell in row]) for row in ws.iter_rows()])
)

茴香豆的另一种写法

python 复制代码
# 遍历工作表中的所有行
for row in ws.values:
    # 遍历行中的每个值
    for value in row:
        print(value)

茴香豆的另另一种写法

python 复制代码
# 遍历工作表中的所有行
for row in ws.iter_rows(values_only=True):
    # 遍历行中的每个值
    for value in row:
        print(value)

5.1.5 指定 sheet 对象进行填写

注意!如果有多个 sheet 对象的时候,需要先获取指定 sheet 对象再进行写入

python 复制代码
from openpyxl import Workbook

wb = Workbook()

ws = wb.active
ws.title = "人员表"  # 修改当前工作表的名称
csb = wb.create_sheet("测试表", 0)  # 正数索引:从0开始,指定插入位置

treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]
for row in treeData:
    ws.append(row)

wb.save("sample.xlsx")
print("创建成功!")

我新增了一个"测试表",但是却只向"人员表"写入了内容;如果我们想给测试表写入内容就应该向 csb 变量进行赋值,把 ws 改为 csb 即可

python 复制代码
for row in treeData:
    csb.append(row)

如果是读取工作簿的时候,就可以通过 Workbook['表名'] 获取到工作表对象,然后进行赋值

python 复制代码
# 根据名称获取到指定的 sheet 对象
csb = wb['测试表']

5.1.6 插入图片

通过 add_image() 方法向 Excel 工作表中插入图片

5.1.6.1 基本用法
python 复制代码
from openpyxl import Workbook
from openpyxl.drawing.image import Image

wb = Workbook()

ws = wb.active
ws.title = "人员表"

img = Image("img.png")  # 加载图片
ws.add_image(img, "A1") # 在指定的单元格位置插入图片

wb.save("sample.xlsx")
print("创建成功!")

直接加载图片的话,图片会太大导致超出单元格,我们可以手动设置图片大小

5.1.6.2 设置图片尺寸
python 复制代码
from openpyxl import Workbook
from openpyxl.drawing.image import Image

wb = Workbook()

ws = wb.active
ws.title = "人员表"

img = Image("img.png")  # 加载图片
# 调整图片尺寸(单位为像素)
img.width = 150
img.height = 100
ws.add_image(img, "A1") # 在指定的单元格位置插入图片

wb.save("sample.xlsx")
print("创建成功!")

直接写死图片宽高需要注意图片的比例,否则会出现拉伸

5.1.6.3 按比例缩放图片
python 复制代码
from openpyxl import Workbook
from openpyxl.drawing.image import Image
from PIL import Image as PILImage

wb = Workbook()

ws = wb.active
ws.title = "人员表"

img = Image("img.png")  # 在指定的单元格位置插入图片

# 按比例缩放图片
pil_img = PILImage.open("img.png")
width, height = pil_img.size
scale = 0.5  # 缩小为原来的一半
img.width = width * scale
img.height = height * scale
ws.add_image(img, "A1")

wb.save("sample.xlsx")
print("创建成功!")

将图片缩小一倍大小

5.1.7 插入图表

5.2 单元格的内容修改

这里引用前面 [5.1.3 通过 append 批量填写](#5.1.3 通过 append 批量填写 "#513-%E9%80%9A%E8%BF%87-append-%E6%89%B9%E9%87%8F%E5%A1%AB%E5%86%99") 章节的内容创建一个工作簿

然后加载工作簿,对单元格的内容进行修改

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

# 给单元格填写值(通过工作表的键)
ryb["A2"].value = "小明1"

# 给单元格填写值(通过行列表示法):row 行,column 列,value 值
ryb.cell(row=3, column=1, value="小白1")

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

成功修改单元格内容

5.3 插入和删除行和列

5.3.1 插入行

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

ryb.insert_rows(1)  # 在第 1 行的位置插入 1 行
ryb.insert_rows(3, 2)  # 在第 3 行的位置插入 2 行

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

5.3.2 删除行

注意!删除的时候应该从下往上倒着删。否则前面的被删除了行数减少了,再按照之前插入行的行号进行删除会导致删错行

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

ryb.delete_rows(3, 2)  # 在第 3 行的位置删除 2 行
ryb.delete_rows(1)  # 在第 1 行的位置删除 1 行

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

5.3.3 插入列

读取工作簿并进行列的插入

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

ryb.insert_cols(1)  # 在第 1 列的位置插入 1 列

ryb.insert_cols(3, 2)  # 在第 3 列的位置插入 2 列

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

成功插入列

5.3.4 删除列

注意!删除的时候应该从右往左倒着删。否则左边的被删除了列数减少了,再按照之前插入列的列号进行删除会导致删错列

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

ryb.delete_cols(3, 2)  # 在第 3 列开始的 2 列
ryb.delete_cols(1)  # 在第 1 列的位置删除 1 列

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

5.4 合并和撤销合并单元格

5.4.1 合并单元格

通过 merge_cells() 函数合并单元格

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

# 合并第1行,A列到C列 (A1:C1)
ryb.merge_cells("A1:C1")
# 合并一个矩形区域,从A2到B3 (A2:B3)
ryb.merge_cells("A2:B3")

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

5.4.2 撤销合并单元格

python 复制代码
from openpyxl import load_workbook

wb = load_workbook(filename="sample.xlsx")  # 加载 excel 文件
ryb = wb["人员表"]  # 获取名称为 "人员表" 的工作表

# 拆分A1:C1区域
ryb.unmerge_cells("A1:C1")
# 拆分A2:B3区域
ryb.unmerge_cells("A2:B3")

wb.save("sample.xlsx")  # 保存 excel
print("创建成功!")

5.5 设置单元格的样式

可以指定 Excel 单元格的样式,比如字体类型,字号大小,字体颜色,字体是否加粗/斜体,单元格的边框样式等

5.5.1 font(字体)

参数 说明 类型 默认值 可选值
name 字体名称 str Calibri 任意系统字体,如 Arial、宋体
size 字号 float / int 11 任意数字(如 10、12、14)
bold 是否加粗 bool False True / False
italic 是否斜体 bool False True / False
vertAlign 上下标 str / None None superscript(上标)、subscript(下标)
underline 下划线 str none none、single、double
strike 删除线 bool False True / False
color 字体颜色(ARGB) str FF000000 8位ARGB,如 FFFF0000(红色)

标题字体加粗,调整字体大小,更改斜体

python 复制代码
from openpyxl import Workbook
from openpyxl.styles import Font

wb = Workbook()
ws = wb.active
treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]

for row in treeData:
    ws.append(row)

# 标题加粗/字体大小/斜体
ft = Font(bold=True, size=14, italic=True)
for row in ws["A1:C1"]:
    for cell in row:
        cell.font = ft

wb.save("sample.xlsx")
print("Excel 文件已保存!")

单独给 "C4" 单元格变为红色

python 复制代码
from openpyxl import Workbook
from openpyxl.styles import Font

wb = Workbook()
ws = wb.active
treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]

for row in treeData:
    ws.append(row)


ws["C4"].font = Font(color="FF0000")

wb.save("sample.xlsx")
print("Excel 文件已保存!")

5.5.2 fill(填充)

填充就是设置单元格的背景颜色

5.5.2.1 纯色填充
python 复制代码
from openpyxl import Workbook
from openpyxl.styles import PatternFill

wb = Workbook()
ws = wb.active
treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]

for row in treeData:
    ws.append(row)

for row in ws["A1:C1"]:
    for cell in row:
        # 创建纯色填充对象,设置填充类型为solid,颜色为红色(FF0000)
        cell.fill = PatternFill(fill_type="solid", fgColor="FF0000")

wb.save("sample.xlsx")
print("Excel 文件已保存!")
5.5.2.2 渐变填充
python 复制代码
from openpyxl import Workbook
from openpyxl.styles import GradientFill

wb = Workbook()
ws = wb.active
treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]

for row in treeData:
    ws.append(row)

for row in ws["A1:C1"]:
    for cell in row:
        cell.fill = GradientFill(
            type="linear",
            degree=90,  # 渐变角度(0-360)
            stop=("FF0000", "0000FF"),  # 从红到蓝
        )

wb.save("sample.xlsx")
print("Excel 文件已保存!")
5.5.2.3 多色渐变

颜色值后面多加一个颜色即可

python 复制代码
stop=("FF0000", "00FF00", "0000FF"),  # 红→绿→蓝
5.5.2.4 渐变填充高级应用
python 复制代码
def advanced_gradient_fill():
    """高级渐变填充示例"""
    from openpyxl import Workbook
    from openpyxl.styles import GradientFill

    wb = Workbook()
    ws = wb.active

    # 1. 多色线性渐变
    ws["A1"] = "多色线性渐变"
    multi_color_gradient = GradientFill(
        type="linear", degree=45, stop=("FF0000", "00FF00", "0000FF", "FFFF00")
    )
    ws["A1"].fill = multi_color_gradient

    # 2. 对角线渐变
    ws["B1"] = "对角线渐变"
    diagonal_gradient = GradientFill(
        type="linear",
        degree=135,  # 135度对角线
        stop=("FF0000", "0000FF"),
    )
    ws["B1"].fill = diagonal_gradient

    # 3. 垂直渐变
    ws["C1"] = "垂直渐变"
    vertical_gradient = GradientFill(
        type="linear",
        degree=0,  # 垂直方向
        stop=("FFFFFF", "000000"),
    )
    ws["C1"].fill = vertical_gradient

    # 4. 水平渐变
    ws["D1"] = "水平渐变"
    horizontal_gradient = GradientFill(
        type="linear",
        degree=90,  # 水平方向
        stop=("FF9999", "990000"),
    )
    ws["D1"].fill = horizontal_gradient

    # 5. 彩虹渐变
    ws["E1"] = "彩虹渐变"
    rainbow_gradient = GradientFill(
        type="linear",
        degree=0,
        stop=("FF0000", "FF7F00", "FFFF00", "00FF00", "0000FF", "4B0082", "9400D3"),
    )
    ws["E1"].fill = rainbow_gradient

    # 调整行高和列宽
    ws.row_dimensions[1].height = 50
    for col in range(1, 6):
        ws.column_dimensions[chr(64 + col)].width = 20

    wb.save("sample.xlsx")
    print("高级渐变填充示例已创建")


advanced_gradient_fill()
5.5.2.5 填充与样式的组合应用
python 复制代码
def styled_table_with_fill():
    """创建带样式的表格"""
    from openpyxl import Workbook
    from openpyxl.styles import Alignment, Border, Font, PatternFill, Side

    wb = Workbook()
    ws = wb.active
    ws.title = "销售报表"

    # 定义样式
    styles = {
        "header": {
            "fill": PatternFill(fill_type="solid", fgColor="366092"),
            "font": Font(name="微软雅黑", size=12, bold=True, color="FFFFFF"),
            "alignment": Alignment(horizontal="center", vertical="center"),
            "border": Border(
                left=Side(style="thin"),
                right=Side(style="thin"),
                top=Side(style="thin"),
                bottom=Side(style="thin"),
            ),
        },
        "subheader": {
            "fill": PatternFill(fill_type="solid", fgColor="D9E1F2"),
            "font": Font(name="宋体", size=11, bold=True),
            "alignment": Alignment(horizontal="center", vertical="center"),
            "border": Border(
                left=Side(style="thin"),
                right=Side(style="thin"),
                top=Side(style="thin"),
                bottom=Side(style="thin"),
            ),
        },
        "highlight": {
            "fill": PatternFill(fill_type="solid", fgColor="FFF2CC"),
            "font": Font(bold=True),
            "alignment": Alignment(horizontal="center", vertical="center"),
        },
        "even_row": {"fill": PatternFill(fill_type="solid", fgColor="F2F2F2")},
        "odd_row": {"fill": PatternFill(fill_type="solid", fgColor="FFFFFF")},
        "total_row": {
            "fill": PatternFill(fill_type="solid", fgColor="C6EFCE"),
            "font": Font(bold=True, color="006100"),
            "alignment": Alignment(horizontal="right", vertical="center"),
        },
    }

    # 表头
    headers = ["季度", "产品A", "产品B", "产品C", "产品D", "总计"]

    for col, header in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col, value=header)
        for attr, value in styles["header"].items():
            setattr(cell, attr, value)

    # 数据
    data = [
        ["Q1", 12000, 15000, 8000, 10000],
        ["Q2", 14000, 16000, 9000, 11000],
        ["Q3", 13000, 17000, 8500, 12000],
        ["Q4", 15000, 18000, 9500, 13000],
    ]

    for row_idx, row_data in enumerate(data, 2):
        # 交替行颜色
        row_style = styles["even_row"] if row_idx % 2 == 0 else styles["odd_row"]

        for col_idx, value in enumerate(row_data, 1):
            cell = ws.cell(row=row_idx, column=col_idx, value=value)
            cell.fill = row_style["fill"]

            # 设置对齐
            if col_idx == 1:  # 第一列左对齐
                cell.alignment = Alignment(horizontal="left", vertical="center")
            else:  # 其他列右对齐
                cell.alignment = Alignment(horizontal="right", vertical="center")

    # 计算每列的总计
    total_row = len(data) + 2
    ws.cell(row=total_row, column=1, value="总计").fill = styles["total_row"]["fill"]

    for col in range(2, len(headers) + 1):
        # 计算公式
        col_letter = chr(64 + col)
        formula = f"=SUM({col_letter}2:{col_letter}{total_row - 1})"

        cell = ws.cell(row=total_row, column=col, value=formula)
        cell.fill = styles["total_row"]["fill"]
        cell.font = styles["total_row"]["font"]
        cell.alignment = styles["total_row"]["alignment"]

    # 设置边框
    thin_border = Border(
        left=Side(style="thin"),
        right=Side(style="thin"),
        top=Side(style="thin"),
        bottom=Side(style="thin"),
    )

    for row in range(1, total_row + 1):
        for col in range(1, len(headers) + 1):
            ws.cell(row=row, column=col).border = thin_border

    # 高亮显示最大值
    max_value = 0
    max_cell = None

    for row in range(2, total_row):
        for col in range(2, len(headers) + 1):
            value = ws.cell(row=row, column=col).value
            if value and value > max_value:
                max_value = value
                max_cell = ws.cell(row=row, column=col)

    if max_cell:
        max_cell.fill = styles["highlight"]["fill"]
        max_cell.font = styles["highlight"]["font"]

    # 调整列宽
    column_widths = [10, 12, 12, 12, 12, 12]
    for i, width in enumerate(column_widths, 1):
        ws.column_dimensions[chr(64 + i)].width = width

    # 调整行高
    for row in range(1, total_row + 1):
        ws.row_dimensions[row].height = 25

    wb.save("sample.xlsx")
    print("样式表格已创建")


styled_table_with_fill()

高亮显示最大值

5.5.3 border(边框)

python 复制代码
from openpyxl import Workbook
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter


def create_border_demo():
    """创建边框示例工作簿"""
    # 创建新工作簿
    wb = Workbook()
    ws = wb.active
    ws.title = "边框示例"

    # 定义各种边框样式
    thin_side = Side(style="thin", color="000000")
    medium_side = Side(style="medium", color="000000")
    thick_side = Side(style="thick", color="000000")
    dashed_side = Side(style="dashed", color="000000")
    double_side = Side(style="double", color="000000")
    hair_side = Side(style="hair", color="000000")
    dotted_side = Side(style="dotted", color="000000")
    dashDot_side = Side(style="dashDot", color="000000")
    dashDotDot_side = Side(style="dashDotDot", color="000000")

    # 定义不同颜色的边
    red_side = Side(style="thin", color="FF0000")
    green_side = Side(style="thin", color="00FF00")
    blue_side = Side(style="thin", color="0000FF")
    yellow_side = Side(style="thin", color="FFFF00")

    # 1. 基本边框样式
    row = 1
    ws.cell(row=row, column=1, value="边框样式示例")
    ws.merge_cells(f"A{row}:D{row}")
    ws[f"A{row}"].alignment = Alignment(horizontal="center", vertical="center")
    ws[f"A{row}"].font = Font(bold=True, size=14)
    ws[f"A{row}"].fill = PatternFill(fill_type="solid", fgColor="E6F3FF")

    row += 2

    # 定义不同边框样式
    border_styles = [
        (
            "thin",
            "细线边框",
            Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side),
        ),
        (
            "medium",
            "中等边框",
            Border(
                left=medium_side, right=medium_side, top=medium_side, bottom=medium_side
            ),
        ),
        (
            "thick",
            "粗线边框",
            Border(
                left=thick_side, right=thick_side, top=thick_side, bottom=thick_side
            ),
        ),
        (
            "dashed",
            "虚线边框",
            Border(
                left=dashed_side, right=dashed_side, top=dashed_side, bottom=dashed_side
            ),
        ),
        (
            "double",
            "双线边框",
            Border(
                left=double_side, right=double_side, top=double_side, bottom=double_side
            ),
        ),
        (
            "hair",
            "发丝边框",
            Border(left=hair_side, right=hair_side, top=hair_side, bottom=hair_side),
        ),
        (
            "dotted",
            "点线边框",
            Border(
                left=dotted_side, right=dotted_side, top=dotted_side, bottom=dotted_side
            ),
        ),
        (
            "dashDot",
            "点划线边框",
            Border(
                left=dashDot_side,
                right=dashDot_side,
                top=dashDot_side,
                bottom=dashDot_side,
            ),
        ),
        (
            "dashDotDot",
            "双点划线边框",
            Border(
                left=dashDotDot_side,
                right=dashDotDot_side,
                top=dashDotDot_side,
                bottom=dashDotDot_side,
            ),
        ),
    ]

    for i, (style_name, description, border) in enumerate(border_styles):
        current_row = row + (i // 4) * 2
        current_col = (i % 4) * 3 + 1

        # 设置单元格
        cell = ws.cell(row=current_row, column=current_col, value=description)
        cell.border = border
        cell.alignment = Alignment(
            horizontal="center", vertical="center", wrap_text=True
        )
        cell.fill = PatternFill(fill_type="solid", fgColor="F2F2F2")

        # 调整列宽
        ws.column_dimensions[get_column_letter(current_col)].width = 15

        # 设置示例单元格
        example_cell = ws.cell(
            row=current_row, column=current_col + 1, value=style_name
        )
        example_cell.border = border
        example_cell.alignment = Alignment(horizontal="center", vertical="center")
        ws.column_dimensions[get_column_letter(current_col + 1)].width = 12

    row = max([row + (i // 4) * 2 for i in range(len(border_styles))]) + 3

    # 2. 不同边的边框
    ws.cell(row=row, column=1, value="不同边的边框")
    ws.merge_cells(f"A{row}:D{row}")
    ws[f"A{row}"].alignment = Alignment(horizontal="center", vertical="center")
    ws[f"A{row}"].font = Font(bold=True, size=14)
    ws[f"A{row}"].fill = PatternFill(fill_type="solid", fgColor="E6F3FF")

    row += 2

    side_borders = [
        ("只有左边框", Border(left=thick_side)),
        ("只有右边框", Border(right=thick_side)),
        ("只有上边框", Border(top=thick_side)),
        ("只有下边框", Border(bottom=thick_side)),
        ("左右边框", Border(left=medium_side, right=medium_side)),
        ("上下边框", Border(top=medium_side, bottom=medium_side)),
        (
            "四边不同",
            Border(left=red_side, right=green_side, top=blue_side, bottom=yellow_side),
        ),
        (
            "外粗内细",
            Border(
                left=Side(style="thick", color="000000"),
                right=Side(style="thin", color="000000"),
                top=Side(style="thick", color="000000"),
                bottom=Side(style="thin", color="000000"),
            ),
        ),
    ]

    for i, (description, border) in enumerate(side_borders):
        current_row = row + (i // 4) * 2
        current_col = (i % 4) * 3 + 1

        cell = ws.cell(row=current_row, column=current_col, value=description)
        cell.border = border
        cell.alignment = Alignment(
            horizontal="center", vertical="center", wrap_text=True
        )
        cell.fill = PatternFill(fill_type="solid", fgColor="FFF2CC")

        ws.column_dimensions[get_column_letter(current_col)].width = 15

    row = max([row + (i // 4) * 2 for i in range(len(side_borders))]) + 3

    # 3. 表格边框示例
    ws.cell(row=row, column=1, value="表格边框示例")
    ws.merge_cells(f"A{row}:F{row}")
    ws[f"A{row}"].alignment = Alignment(horizontal="center", vertical="center")
    ws[f"A{row}"].font = Font(bold=True, size=14)
    ws[f"A{row}"].fill = PatternFill(fill_type="solid", fgColor="E6F3FF")

    row += 2

    # 创建表格数据
    headers = ["姓名", "年龄", "城市", "部门", "薪资"]
    data = [
        ["张三", 28, "北京", "技术部", 15000],
        ["李四", 32, "上海", "市场部", 18000],
        ["王五", 25, "广州", "人事部", 12000],
        ["赵六", 30, "深圳", "财务部", 16000],
        ["钱七", 27, "杭州", "技术部", 14000],
    ]

    # 表头边框(粗边框)
    header_border = Border(
        left=Side(style="medium", color="366092"),
        right=Side(style="medium", color="366092"),
        top=Side(style="medium", color="366092"),
        bottom=Side(style="medium", color="366092"),
    )

    header_fill = PatternFill(fill_type="solid", fgColor="366092")
    header_font = Font(bold=True, color="FFFFFF")

    # 写入表头
    for col, header in enumerate(headers, 1):
        cell = ws.cell(row=row, column=col, value=header)
        cell.border = header_border
        cell.fill = header_fill
        cell.font = header_font
        cell.alignment = Alignment(horizontal="center", vertical="center")
        ws.column_dimensions[get_column_letter(col)].width = 12

    row += 1

    # 数据行边框(细边框)
    data_border = Border(
        left=Side(style="thin", color="000000"),
        right=Side(style="thin", color="000000"),
        top=Side(style="thin", color="000000"),
        bottom=Side(style="thin", color="000000"),
    )

    # 写入数据
    for i, row_data in enumerate(data):
        current_row = row + i

        # 交替行颜色
        if i % 2 == 0:
            row_fill = PatternFill(fill_type="solid", fgColor="FFFFFF")
        else:
            row_fill = PatternFill(fill_type="solid", fgColor="F2F2F2")

        for col, value in enumerate(row_data, 1):
            cell = ws.cell(row=current_row, column=col, value=value)
            cell.border = data_border
            cell.fill = row_fill
            cell.alignment = Alignment(horizontal="center", vertical="center")

    row += len(data) + 2

    # 4. 合并单元格边框
    ws.cell(row=row, column=1, value="合并单元格边框")
    ws.merge_cells(f"A{row}:C{row}")
    ws[f"A{row}"].alignment = Alignment(horizontal="center", vertical="center")
    ws[f"A{row}"].font = Font(bold=True, size=14)
    ws[f"A{row}"].fill = PatternFill(fill_type="solid", fgColor="E6F3FF")

    row += 2

    # 合并单元格示例
    merge_border = Border(
        left=Side(style="medium", color="FF0000"),
        right=Side(style="medium", color="00FF00"),
        top=Side(style="medium", color="0000FF"),
        bottom=Side(style="medium", color="FFFF00"),
    )

    # 合并单元格并设置边框
    ws.merge_cells(f"A{row}:C{row + 2}")
    merge_cell = ws.cell(row=row, column=1, value="合并单元格\n边框示例")
    merge_cell.border = merge_border
    merge_cell.alignment = Alignment(
        horizontal="center", vertical="center", wrap_text=True
    )
    merge_cell.fill = PatternFill(fill_type="solid", fgColor="E2EFDA")

    # 调整行高
    ws.row_dimensions[row].height = 60
    ws.row_dimensions[row + 1].height = 60
    ws.row_dimensions[row + 2].height = 60

    row += 4

    # 5. 条件边框示例
    ws.cell(row=row, column=1, value="条件边框示例")
    ws.merge_cells(f"A{row}:D{row}")
    ws[f"A{row}"].alignment = Alignment(horizontal="center", vertical="center")
    ws[f"A{row}"].font = Font(bold=True, size=14)
    ws[f"A{row}"].fill = PatternFill(fill_type="solid", fgColor="E6F3FF")

    row += 2

    # 根据数值设置不同边框
    scores = [85, 92, 78, 60, 95, 45, 88, 72]

    for i, score in enumerate(scores):
        current_row = row + (i // 4)
        current_col = (i % 4) * 3 + 1

        cell = ws.cell(row=current_row, column=current_col, value=f"分数: {score}")
        cell.alignment = Alignment(horizontal="center", vertical="center")

        # 根据分数设置不同边框
        if score >= 90:
            border_color = "00B050"  # 绿色
            fill_color = "C6EFCE"
        elif score >= 80:
            border_color = "FFC000"  # 橙色
            fill_color = "FFEB9C"
        elif score >= 60:
            border_color = "FF0000"  # 红色
            fill_color = "FFC7CE"
        else:
            border_color = "C00000"  # 深红色
            fill_color = "F2DCDB"

        score_border = Border(
            left=Side(style="medium", color=border_color),
            right=Side(style="medium", color=border_color),
            top=Side(style="medium", color=border_color),
            bottom=Side(style="medium", color=border_color),
        )

        cell.border = score_border
        cell.fill = PatternFill(fill_type="solid", fgColor=fill_color)

        ws.column_dimensions[get_column_letter(current_col)].width = 15

    # 保存工作簿
    filename = "sample.xlsx"
    wb.save(filename)
    print(f"边框示例已保存为: {filename}")

    return filename


if __name__ == "__main__":
    # 运行示例
    create_border_demo()

5.5.4 Alignment(对齐)

Alignment 对象用于设置 Excel 单元格的对齐格式

参数 说明 常用取值
horizontal 水平对齐方式 'general' (默认,自动对齐:文本左对齐,数字右对齐) 'left' (左对齐) 'center' (居中) 'right' (右对齐) 'justify' (两端对齐) 'centerContinuous' (跨列居中) 'distributed' (分散对齐)
vertical 垂直对齐方式 'bottom' (默认,靠下对齐) 'top' (靠上对齐) 'center' (垂直居中) 'justify' (垂直两端对齐) 'distributed' (垂直分散对齐)
text_rotation 文本旋转角度 0 (默认,不旋转) 45 (逆时针旋转45度) -45 (顺时针旋转45度) 90 (垂直向上) 180 (倒置)
wrap_text 是否自动换行 False (默认,不换行) True (文本超出单元格宽度时自动换行)
shrink_to_fit 是否缩小字体填充 False (默认,不缩小) True (自动缩小字体以适应单元格宽度)
indent 缩进量(字符数) 0 (默认,无缩进) 12... (向右缩进的字符数)
python 复制代码
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font

wb = Workbook()
ws = wb.active
treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]

for row in treeData:
    ws.append(row)

ft = Font(bold=True, size=14, italic=True)
for row in ws["A1:C1"]:
    for cell in row:
        cell.font = ft
        cell.alignment = Alignment(horizontal="center", vertical="center")


wb.save("sample.xlsx")
print("Excel 文件已保存!")

标题水平垂直居中对齐

6. 保护 Excel 文件

我们可以开启工作簿的保护,禁止添加/删除/重命名工作表

也可以开启工作表的保护,禁止编辑单元格

python 复制代码
from openpyxl import Workbook

wb = Workbook()
ws = wb.active
ws.title = "人员表"  # 修改当前工作表的名称

treeData = [
    ["姓名", "性别", "年龄"],
    ["小明", "男", 25],
    ["小白", "女", 30],
    ["小黑", "男", 35],
]
for row in treeData:
    ws.append(row)

# 保护工作簿结构(禁止添加/删除/重命名工作表)
# 注意:workbookPassword 是"结构保护密码",不是文件打开密码
wb.security.workbookPassword = "123456"  # 设置工作簿结构保护密码
wb.security.lockStructure = True  # 锁定结构,禁止添加/删除/重命名工作表
wb.security.lockWindows = True  # 锁定工作簿窗口位置与大小

# 保护工作表内容
ws.protection.password = "654321"  # 设置工作表保护密码
ws.protection.sheet = True  # 启用工作表保护

# 查看保护设置
print(f"工作簿结构是否被保护: {wb.security.lockStructure}")
print(f"工作表是否被保护: {ws.protection.sheet}")

wb.save("sample.xlsx")
print("创建成功!")

取消工作簿保护:审阅 → 保护工作簿 → 撤销工作簿保护弹窗输入"工作簿保护密码"

取消工作表保护:审阅 → 撤销工作表保护 → 撤销工作表保护弹窗输入"工作表保护密码"

💡 注意:openpyxl 无法设置"打开密码"

Excel 文件的打开密码(即启动 Excel 时要求输入的密码)属于文件级别的加密,openpyxl 不支持此功能。若要实现,需在生成 Excel 文件后使用第三方工具(如 pyminizip)打包加密。

相关推荐
2401_865439632 小时前
C#怎么将控制台输出保存到TXT_C#如何重定向输出流【源码】
jvm·数据库·python
2301_815279522 小时前
Golang和Node.js哪个适合后端_Golang Node对比教程【实战】
jvm·数据库·python
m0_748839492 小时前
CSS如何制作圆形头像盒子_设置border-radius-50%
jvm·数据库·python
深蓝海拓2 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(八)在上位机中解析上行报文
网络·笔记·python·学习·plc
Wyz201210242 小时前
如何进行SQL字符串大小写转换_运用UPPER与LOWER函数
jvm·数据库·python
qq_189807032 小时前
SQL报表临时表过大问题_临时表生成机制优化
jvm·数据库·python
遇事不決洛必達2 小时前
某方数据库protobuf详解
爬虫·python·protobuf
XY_墨莲伊3 小时前
【编译原理】实验二:基于有穷自动机FA词法分析器设计与实现
c语言·开发语言·c++·python
qq_452396233 小时前
【工程实战】第三篇:接口自动化 —— Requests 的工业级封装:Session 关联、日志与断言
python·自动化