Python openpyxl 给 Excel 添加条件格式:自动高亮数据区间与行列
2026/9/4 21:29:47 网站建设 项目流程

在实际业务中,我们经常需要把表格导出为 Excel,并让它自带“高亮效果”。比如销售报表要标记超标数据、成绩表要显示高低分区间、排班表要区分单双周。如果这些操作停留在手工阶段,每次数据更新后都需要反复重复设置,效率会非常低。

Python 的openpyxl库可以帮助我们直接操作 Excel 文件,把“条件格式”写入工作表的指定区域。数据一旦更新,条件格式依然存在,配合 Pandas 批量处理数据非常高效。

今天这篇教程,将围绕Python 操作 Excel 条件格式展开,分为三个常用场景:

  • 高亮交替行颜色,提升长表格可读性;
  • 高亮每个科目/列中的最大值和最小值
  • 根据数值范围(比如 Total Score 在某区间内)动态填充颜色。

除此之外,还会介绍色阶(Color Scale)最低分/不及格标注等延伸技巧,并提供一份完整可运行的 Python 脚本,帮你在真实项目中落地。

本文适合这样的读者:

  • 会 Python 基础语法,但不太熟悉openpyxl条件格式 API 的开发者;
  • 需要批量生成 Excel 报表、数据分析结果导出文件等技术同学;
  • 想减少手工处理 Excel 格式、把重复步骤自动化的效率爱好者。

学完本文后,你能够独立完成“创建 Excel → 写入数据 → 添加条件格式 → 保存文件 → 验证效果”的完整闭环。

1. Excel 条件格式到底能做什么

1.1 为什么不用传统单元格填色

很多同学第一反应是:给某个单元格填充颜色,直接用PatternFill不就好了?

from openpyxl.styles import PatternFill fill = PatternFill(start_color='FFFF00', end_color='FFFF00', fill_type='solid') ws['A1'].fill = fill

这种方式确实“能上色”,但它是静态的。比如:你先把 A1 的值设置成 90,然后填黄色,之后 A1 变成 50,黄色永远不会消失。可如果 Excel 里使用的是真正的条件格式,单元格颜色会根据值的变化自动更新,完全不需要重新执行 Python 脚本。

换句话说:

  • 静态填色适合“输出时已经确定结果”的场景;
  • 条件格式适合“交付后仍希望 Excel 自动响应数据变化”的场景。

当我们用 Python 生成模板、报表,或者给同事交付一份有筛选/输入属性的 Excel 时,条件格式才是更贴近真实需求的技术方案。

1.2 条件格式在业务中的常见应用场景

条件格式不是 Excel 的炫技功能,它解决的是**“数据可视化 + 数据提醒”**问题。

场景条件格式策略业务收益
销售业绩表高亮超过 100 万的记录快速定位超额订单
学生成绩单标红不及格科目直观展示薄弱科目
库存报表对低于安全库存的产品填色及时补货
日程排班区分奇偶行或班次类型减少误读
财务对账高亮差异大于阈值的行快速复核重点项目

这类格式变化如果交给业务人员手工完成,不仅耗时,还容易遗漏;如果和数据逻辑混在一个脚本里,后续维护也很痛苦。比较规范的做法是:Python 只负责生成数据和写入规则,Excel 本身负责数据变化后的自动展现

1.3 本文的技术边界

本文使用openpyxl实现条件格式,主要会接触以下 API:

  • Worksheet.conditional_formatting.add()
  • CellIsRule
  • FormulaRule
  • ColorScaleRule
  • PatternFillFontBorder

不需要使用 VBA 或 Excel COM 组件。只要安装了 Python 和openpyxl,就可以跨平台运行,适合 Linux 服务器上的报表服务和 CI/CD 流程。

2. 环境准备与项目目录

2.1 安装 Python 和 openpyxl

如果本机还没有安装 Python,请先安装 Python 3.8 或以上版本。命令行中输入:

python --version

确认有 Python 环境后,执行:

pip install openpyxl

如果希望和 Pandas 配合,可以安装:

pip install pandas openpyxl

openpyxl是 Excel.xlsx文件操作库,它不依赖 Excel 软件本身。因此哪怕是在 Linux 服务器上,也可以生成带条件格式的 Excel 文件。

2.2 示例项目结构

为了让教程更贴近实际开发,我建议按下面结构试验:

excel_conditional_formatting/ ├── output/ ├── create_report.py └── 需求说明.md

create_report.py是我们要运行的主脚本,output/目录存放生成的 Excel 文件。

如果你暂时不想创建项目目录,也可以把 Python 脚本放在任意位置,只要修改保存路径即可。本文示例将演示完整代码,方便拷贝执行。

2.3 openpyxl 版本说明

openpyxlconditional_formatting相关 API 从 2.5 版本后已相对稳定。本文示例基于openpyxl 3.x的通用写法。

由于各版本 API 细节略有差异,如果你的代码运行报错,请优先执行:

pip install --upgrade openpyxl

来确保依赖版本不落后于示例。

3. openpyxl 条件格式核心概念

3.1 最小插入流程

使用openpyxl给 Excel 添加条件格式,本质上只有两步:

  1. 构造一个Rule 规则对象
  2. 把这个规则通过conditional_formatting.add()应用到某个单元格区域。

例如,我们要让 A1:A10 中大于 80 的值显示绿色:

from openpyxl import Workbook from openpyxl.formatting.rule import CellIsRule from openpyxl.styles import PatternFill wb = Workbook() ws = wb.active # 写入测试数据 for i in range(1, 11): ws[f'A{i}'] = i * 10 green_fill = PatternFill(start_color='C6EFCE', end_color='C6EFCE', fill_type='solid') # 构造条件格式规则,大于 80 时应用公式或格式 ws.conditional_formatting.add( 'A1:A10', CellIsRule( operator='greaterThan', formula=['80'], fill=green_fill ) ) wb.save('demo1.xlsx')

运行这段代码后,打开demo1.xlsx,数据大于 80 的单元格会自动变为绿底。如果手动把 82 改成 60,颜色也会立刻消失,这就是“条件格式”与“静态填充”的最大区别。

3.2 理解“规则作用区域”的字符串

ws.conditional_formatting.add(区域, 规则)的“区域”可以是如下形式:

  • 'A1:A10':单列单元格区域;
  • 'A1:F100':矩形区域;
  • 'A1:F1':单行区域;
  • 'A:C':整列区域。

这里的区域是指规则的作用范围,并不等于公式参与运算的范围。公式仍然由表达式中的引用所决定。

例如:

ws.conditional_formatting.add( 'A1:A10', CellIsRule( operator='greaterThan', formula=['=MAX($A$1:$A$10)'], # 也可以不带等号 fill=green_fill ) )

这个规则作用在 A1:A10,判断每个单元格是否大于 A1:A10 的最大值。虽然我们很少这么做,但概念上要区分清楚。

3.3 Rule 表达式中是否带等号

很多同学在写FormulaRule时经常困惑:公式里要不要带一个等号?

openpyxl文档和多数示例中,formula参数可以写['MOD(ROW(),2)=0'],不带头等号,也可以写成['=MOD(ROW(),2)=0'],效果等价。为了保险起见,统一建议不写等号,如下:

FormulaRule(formula=['MOD(ROW(),2)=0'])

如果写CellIsRule,公式只填入数值或函数表达式:

CellIsRule(operator='between', formula=['60', '90'])

最保险的做法是在本地 Excel 里先录制一个条件格式,再查看对应 XML,但本文不会深入到 XML 层面。按照上述写法,绝大多数场景都可正常运作。

3.4 动态区域推荐使用 Excel 表而不是开放引用

如果你写的是:

ws.conditional_formatting.add('A1:A100', ...)

当数据超过 100 行时,新增行不会自动应用规则。实际项目中建议给 Excel 数据区域插入“表格(Table)”,或者把范围写得足够大,例如A1:F10000,但过大范围会影响文件体积和运算性能。

如果使用openpyxl让你手动控制范围,推荐先获得数据行数:

max_row = ws.max_row ws.conditional_formatting.add(f'A1:F{max_row}', ...)

这样在写数据之后添加规则,能天然匹配当前数据量。

4. 实战案例:学生成绩表如何加条件格式

为了让三个核心需求更有代入感,我设计一个学生成绩表案例。

4.1 业务需求

有一份学生成绩单,包含字段:

姓名语文数学英语总分
张三889276256
李四708590245
...............

交付前需要实现 5 个可视化动作:

  1. 整行隔行变色,让长表格更易读;
  2. 每科(语文、数学、英语)的最高分用绿色背景高亮;
  3. 每科最低分用橙色背景高亮;
  4. 总分在 240~270 分区间的记录,所在行添加黄色底纹;
  5. 总分低于 200 的单元格标红。

4.2 创建基础数据与表头

我们先新建一个脚本文件:create_report.py,写入导入和数据初始化。

# 文件路径:excel_conditional_formatting/create_report.py from openpyxl import Workbook from openpyxl.styles import PatternFill, Font, Alignment, Border, Side from openpyxl.formatting.rule import CellIsRule, FormulaRule from openpyxl.utils import get_column_letter wb = Workbook() ws = wb.active ws.title = "成绩分析" # 表头 headers = ["姓名", "语文", "数学", "英语", "总分"] ws.append(headers) # 模拟数据 raw_data = [ ("张三", 88, 92, 76, 256), ("李四", 70, 85, 90, 245), ("王五", 60, 66, 58, 184), ("赵六", 95, 80, 88, 263), ("孙七", 82, 91, 73, 246), ("周八", 79, 64, 85, 228), ("吴九", 93, 98, 96, 287), ("郑十", 58, 72, 69, 199), ("冯十一", 74, 89, 61, 224), ("陈十二", 90, 77, 82, 249), ] for row in raw_data: ws.append(list(row))

表格结构为:

  • 第 1 行:表头;
  • 第 2~11 行:10 条学生成绩数据;
  • 数据总行数:10;
  • 最后一行行号:ws.max_row,值为 11。

4.3 设计样式常量

我们首先要定义不同的填充色。实际项目中,你可以参考 Excel 内置条件格式颜色的十六进制值。

# 样式常量 header_font = Font(bold=True, color="FFFFFF", size=11) header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid") center_alignment = Alignment(horizontal="center", vertical="center") # 交替行颜色:浅蓝 alternate_fill = PatternFill(start_color="D9E1F2", end_color="D9E1F2", fill_type="solid") # 最高分颜色:绿底深绿字 max_fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid") max_font = Font(color="006100", bold=True) # 最低分颜色:橙底深橙字 min_fill = PatternFill(start_color="FFEB9C", end_color="FFEB9C", fill_type="solid") min_font = Font(color="9C6500", bold=True) # 总分范围:黄底 range_fill = PatternFill(start_color="FFF2CC", end_color="FFF2CC", fill_type="solid") # 低分红色:红底深红字 low_fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid") low_font = Font(color="9C0006", bold=True)

注意:PatternFillstart_colorend_color在纯色填充下保持一致即可。为什么要写fill_type="solid"?因为 openpyxl 如果只是给出两个颜色值但不声明 solid,可能在 Excel 中显示不出纯色。

4.4 设置表头样式并冻结窗格

为了让交付效果更专业,我们可以顺手做:

# 设置表头样式 for col_idx, _ in enumerate(headers, start=1): cell = ws.cell(row=1, column=col_idx) cell.font = header_font cell.fill = header_fill cell.alignment = center_alignment # 设置列宽 for col_idx in range(1, len(headers) + 1): ws.column_dimensions[get_column_letter(col_idx)].width = 12 # 冻结首行,方便向下翻阅 ws.freeze_panes = "A2" # 表头加边框 thin_border = Border( left=Side(style="thin", color="B4C6E7"), right=Side(style="thin", color="B4C6E7"), top=Side(style="thin", color="B4C6E7"), bottom=Side(style="thin", color="B4C6E7"), ) for row in ws.iter_rows(min_row=1, max_row=ws.max_row, max_col=ws.max_column): for cell in row: cell.border = thin_border

这里的代码虽然和条件格式不直接相关,但在一份真实交付文件中很常见,因为只有同时完善字体、列宽、对齐和边框,导出的表格才具备“可直接发给他人”的质量。

5. 高亮交替行颜色(斑马纹)

5.1 实现隔行变色的核心思路

交替行颜色的实现方案有很多种:

  1. 每写入一行数据,就对该行整行填充静态颜色;
  2. 使用条件格式,利用ROW()函数判断当前行号奇偶;
  3. 使用 Excel 表格的“带状行”功能(Table Style)。

本文重点讲第 2 种,因为它不需要重写数据,并且当用户插入/删除行时颜色也能自动适配(在一定范围内)。

条件公式如下:

MOD(ROW(),2)=1
  • ROW():返回当前单元格所在行号;
  • MOD(ROW(),2):行号对 2 取余;
  • 等于 1 表示奇数行,等于 0 表示偶数行。

如果第一行是表头,我们一般想让第 2、4、6... 行有底色,也就是偶数行。公式就应该是:

MOD(ROW(),2)=0

也可以反过来,为奇数行填充浅色。重点是不要把条件区域包含表头,否则表头也可能被染色。

5.2 将斑马纹作用到整行

假设数据区域为 A2:E11,我们要让该区域内的偶数行变浅蓝色。

# 数据区域最后一行行号 last_row = ws.max_row # 对 A2:E11 添加条件格式 ws.conditional_formatting.add( f'A2:E{last_row}', FormulaRule( formula=['MOD(ROW(),2)=0'], fill=alternate_fill ) )

这段代码会每行独立判断,理论上该规则受区域限制,只对 A 到 E 列对应行生效。如果要让整行(比如 A 到 G)显示,区域就要写到 G。

5.3 更安全的“从第三行开始”技巧

如果表格设计了标题行和合并单元格,通常我们不希望第一块数据行也被隔行染色。更灵活的做法是用“序号”列或辅助公式来判断。但最简单的是认清起点行号。

假设数据从第 3 行开始,那么该行需要染色时,条件公式使用MOD(ROW(),2)=1还是=0,取决于你想让第 3 行是否有颜色。如果第 3 行染色,那条件是:

MOD(ROW(),2)=1

因为 3 对 2 取余等于 1。

这里需要特别提醒:不要盲目的复制网上的公式,先想清楚自己的表头在第几行、数据开始在哪一行、哪一行需要被高亮。否则会出现隔行颜色颠倒或从中间错位的问题。

5.4 可以搭配的“悬浮效果”思路

虽然 Excel 条件格式不太适合“鼠标悬浮行高亮”,但我们可以利用同样的公式原理实现“奇数列、偶数列”的交替颜色。例如将公式改成:

MOD(COLUMN(),2)=0

就可以让 B、D、F... 列出现底色。这种写法适合做复杂的账单模板,但由于不是今天的重点,这里不展开。

6. 高亮最大值、最小值与范围值

6.1 最大值与最小值的常用规则

在 Excel 手工条件格式里,可以选择“仅对排名靠前或靠后的数值设置格式”,而CellIsRule更适合配合公式实现,因为在 openpyxl 的 API 中,我们可以提交一个函数表达式作为条件。

假设语文成绩在 B 列,范围是 B2:B11。语文最高分标记为绿色:

ws.conditional_formatting.add( f'B2:B{last_row}', CellIsRule( operator='greaterThanOrEqual', formula=[f'MAX($B$2:$B${last_row})'], fill=max_fill, font=max_font ) )
  • operator='greaterThanOrEqual':表示“大于等于”;
  • formula=[f'MAX($B$2:$B${last_row})']:用来求 B2:B11 的最大值;
  • 使用绝对引用$B$2:$B$11,因为它不应该随单元格变化;
  • 规则应用到 B2:B11,循环比较每个单元格的值与该区域最大值。

同样的道理,数学最低分就用橙色标记:

# C列是数学 ws.conditional_formatting.add( f'C2:C{last_row}', CellIsRule( operator='lessThanOrEqual', formula=[f'MIN($C$2:$C${last_row})'], fill=min_fill, font=min_font ) )

如果语文、数学、英语三科都要实现该效果,我们需要分别对 B、C、D 三列添加规则,否则使用同一个规则作用在多个列时,公式会比较混乱。

这段逻辑可以写为循环:

score_columns = { "B": "语文", "C": "数学", "D": "英语", } for col in score_columns: col_range = f"{col}2:{col}{last_row}" # 最高分 ws.conditional_formatting.add( col_range, CellIsRule( operator='greaterThanOrEqual', formula=[f'MAX(${col}$2:${col}${last_row})'], fill=max_fill, font=max_font ) ) # 最低分 ws.conditional_formatting.add( col_range, CellIsRule( operator='lessThanOrEqual', formula=[f'MIN(${col}$2:${col}${last_row})'], fill=min_fill, font=min_font ) )

6.2 最大值、最小值出现多个相同值怎么办

如果一列中有两个相同的最高分,比如语文有两个 95 分,上面的规则会同时高亮这两个 95 分。这不是 bug,因为公式判断条件为“大于等于最大值”,所有等于最大值的单元格都会命中。

如果业务上只希望高亮第一个最大值,则需要配合COUNTIF或数组公式,但在 Excel 条件格式下实现比较复杂。大多数报表场景中,高亮所有最大值/最小值是更能接受的默认行为。

6.3 范围值:Between 与 指定范围

项目标题中的第三个需求是**“范围值”**,通常有两种理解:

  1. 单元格数值在某个区间内,比如总分在 240 到 270 之间时整行高亮;
  2. 单元格的数值加上颜色刻度(ColorScale),例如数值越大颜色越深。

先来看第一种:总分在 E 列,E2:E11。我们希望总分在 240~270 区间的行,其 A~E 列单元格都显示浅黄色。

如果只给 E 列加规则,那么只有总分单元格变色,不符合“整行高亮”的预期。所以我们需要将规则作用到 A2:E11,并使用公式进行跨列判断。

ws.conditional_formatting.add( f'A2:E{last_row}', FormulaRule( formula=[f'AND($E2>=240,$E2<=270)'], fill=range_fill ) )

这里要注意:

  • 区域虽然是 A2:E11,但 FormulaRule 中公式判断的是$E2
  • $E2的含义是:列 E 绝对引用,行号相对引用;
  • 当规则作用到第 3 行单元格时会自动变为$E3
  • AND() &&,一定不要把 Excel 里不支持&&写进去,必须使用AND()函数。

有了这个规则,总分介于 240~270 的学生,整行都会被黄色填充。

6.4 低于 200 分标红

同理,如果我们希望总分小于 200 的单元格用红色提示,可以这样写:

ws.conditional_formatting.add( f'E2:E{last_row}', CellIsRule( operator='lessThan', formula=['200'], fill=low_fill, font=low_font ) )

如果希望整行标红,同样将区域扩大为 A~E 列:

ws.conditional_formatting.add( f'A2:E{last_row}', FormulaRule( formula=[f'$E2<200'], fill=low_fill, font=low_font ) )

6.5 延伸:用 ColorScaleRule 实现双色/三色刻度

有时候“范围值”不是离散区间,而是一种连续视觉梯度。比如希望总分列颜色从高到低自然渐变,可以使用ColorScaleRule

from openpyxl.formatting.rule import ColorScaleRule ws.conditional_formatting.add( f'E2:E{last_row}', ColorScaleRule( start_type='min', start_color='F8696B', # 红色 mid_type='percentile', mid_value=50, mid_color='FFEB84', # 黄色 end_type='max', end_color='63BE7B' # 绿色 ) )

这段代码会给总分列添加三色刻度:最低分红色、中间分黄色、最高分绿色。启动 Excel 后看到的是一行渐变效果,比单纯高亮最大最小值更加直观。

注意:ColorScaleRulestart_typemid_typeend_type可以填写minmaxnumpercentpercentile等。如果你只想要双色刻度,可以创建一个只有 start 和 end 的 ColorScaleRule。

6.6 汇总:本次实战的条件格式代码

把上述片段汇总后,保存文件部分的代码如下:

# 保存文件 output_path = "output/学生成绩条件格式.xlsx" import os os.makedirs("output", exist_ok=True) wb.save(output_path) print(f"文件已生成:{output_path}")

如果你已经按顺序在同一个脚本里写完,现在可以运行:

python create_report.py

运行后,在output/目录下找到学生成绩条件格式.xlsx,打开后可以看到第 2/4/6/8/10 行呈现浅蓝色隔行效果,各科最高分为绿色,最低分为橙色,总分 240~270 的整行是浅黄色,总分低于 200 的行是红色底纹。

7. 条件格式规则与表格样式冲突问题

7.1 手工填充和条件格式同时出现时的优先级

如果你在脚本中既用了PatternFill给某些单元格设了静态底色,又添加了条件格式,当条件格式命中时,Excel 会优先显示条件格式颜色。

举例:

ws['E2'].fill = PatternFill(start_color='FF0000', end_color='FF0000', fill_type='solid')

随后如果 E2 满足“总分 240~270”条件,显示的并不是红色而是规则中设置的颜色。原因在于 Excel 的条件格式默认高于普通填充样式。

这既是优点也是坑:如果你希望某些单元格固定为一种颜色,就不要再给它们加条件格式,或者你给这些单元格所在区域额外加一个“停止如果为真”之类的策略。不过openpyxl对“停止如果为真”的控制较弱,生产环境建议在规则设计阶段就避免冲突。

7.2 区域重叠可能导致深浅色互相覆盖

例如我们有一个规则让 A2:E11 的偶数行变浅蓝,另一个规则让总分在 240~270 的行变黄色。这时第 4 行如果是总分 250,那么它同时满足两个条件。

Excel 默认按照条件格式规则的创建顺序依次判断,如果多个条件都为真,则使用第一个规则格式(在 Excel 界面中上面的规则优先)。因此在脚本中,如果希望范围色优先于隔行色,就要先添加隔行色,再添加范围色

如果颜色表现不符合预期,可以通过调整代码中conditional_formatting.add()的调用顺序来解决。这也是一个很多新手查不出原因的坑。

7.3 需要看清条件格式公式的相对引用

FormulaRule(formula=['MOD(ROW(),2)=0'])ROW()没有带单元格前缀,所以它会跟随“当前单元格”变化。如果你在公式中写成了MOD($A$2,2)=0,那么所有单元格都会拿 A2 的行号去判断,最终导致整片区域要么全变色,要么全不变。

所以在编写条件格式时,请反复检查:

  • 需要“当前行变化”的地方,不要加$行号;
  • 需要“锁定某列/某区域”的地方,不要漏掉$

一个小技巧是:在公式中想象“当前单元格是被选中单元格中的左上角”,然后基于相对位置推导公式。如果你对公式本身不自信,可以先在 Excel 中手写一条条件格式并保存,再用 Python 的只读模式分析其 XML,或者直接打开生成的 xlsx 和预期效果对照。

8. 常见问题与排查思路

这是一个高频问题汇总表,建议遇到问题时优先对照排查。

问题现象常见原因解决思路
生成的 Excel 打开后提示文件损坏openpyxl版本过低或代码保存路径写入异常升级 openpyxl,优先用wb.save()到新路径;不要同时被 Excel 打开后再覆盖
条件格式没有生效添加规则的区域和实际数据区域不一致确认规则区域包含要作用的单元格,数据行号用max_row动态计算
只有第一个单元格变色公式相对引用/绝对引用设置错误检查公式中是否少了$或多加了$;整行判断要使用如$E2<200
整个区域全部变色公式没有正确针对当前单元格例如公式写成ROW(A1)=ROW(),但区域过大导致逻辑错位;需要简化成MOD(ROW(),2)=0
交替行颜色和范围色重叠时显示不符合预期同时满足多个条件,不清楚 Excel 规则优先级调整add()的顺序,或拆分规则区域
颜色很浅或显示不出来PatternFill没有设置fill_type='solid'所有纯色填充统一补上fill_type='solid'
修改 Excel 数据后条件格式不会自动扩展规则作用范围固定为 A1:A100,新增行不在范围生成文件时给规则范围预留足够行数,或使用 Excel 表格对象
WPS/LibreOffice 打开显示效果不一致办公软件对条件格式的公式解析存在差异以 Excel 打开为最终验收标准,涉及复杂公式时先在 Excel 验证

8.1 表格内容为空时,规则仍然存在

openpyxl 只负责将规则写入 XML 文件。即使 A1:F10000 范围内没有数据,规则依然存在,Excel 打开后不会报错。但大范围的条件格式会明显增大文件体积,建议生成前先通过ws.max_row获取有效行数。

8.2 使用 Pandas 读取 Excel 后要不要重写规则

如果你的完整流程是:

Excel读取(openpyxl) → pandas处理 → Excel保存

那么使用 pandas 默认方式保存文件,可能会丢失原来的条件格式。因为 pandas 不会解析并保留所有 openpyxl 对象。解决方案有两种:

  1. 先用openpyxl打开原文件,拿到数据后自行处理,最后wb.save(),保留原规则;
  2. 不要把规则和数据写在一起,先生成新的干净文件再统一添加规则。

推荐第二种,逻辑更清晰,也方便测试。

9. 进阶:封装一个可复用的条件格式管理器

课程只贴代码不讲封装,不利于工程落地。如果项目中有多种报表需要反复使用条件格式,我们可以把这些方法提取到类中。下面我给一个简化版工具类示例。

# 文件路径:excel_conditional_formatting/excel_formatter.py from openpyxl.formatting.rule import CellIsRule, FormulaRule, ColorScaleRule from openpyxl.styles import PatternFill class ExcelConditionalFormatter: def __init__(self, ws): self.ws = ws @staticmethod def make_fill(hex_color: str) -> PatternFill: return PatternFill( start_color=hex_color, end_color=hex_color, fill_type="solid" ) def add_row_striping(self, cell_range: str, color: str = "D9E1F2", even: bool = True) -> None: """为区域添加斑马纹,even=False 表示奇数行变色。""" fill = self.make_fill(color) if even: formula = ["MOD(ROW(),2)=0"] else: formula = ["MOD(ROW(),2)=1"] self.ws.conditional_formatting.add( cell_range, FormulaRule(formula=formula, fill=fill) ) def add_max_min_highlight(self, col_letter: str, start_row: int, end_row: int, max_color: str = "C6EFCE", min_color: str = "FFEB9C") -> None: """对某一列添加最大值绿色、最小值橙色。""" col_range = f"{col_letter}{start_row}:{col_letter}{end_row}" max_fill = self.make_fill(max_color) min_fill = self.make_fill(min_color) self.ws.conditional_formatting.add( col_range, CellIsRule( operator="greaterThanOrEqual", formula=[f"MAX(${col_letter}${start_row}:${col_letter}${end_row})"], fill=max_fill ) ) self.ws.conditional_formatting.add( col_range, CellIsRule( operator="lessThanOrEqual", formula=[f"MIN(${col_letter}${start_row}:${col_letter}${end_row})"], fill=min_fill ) ) def add_range_by_formula(self, cell_range: str, condition_formula: str, color: str = "FFF2CC") -> None: """通过自定义公式添加一个范围/整行规则。""" self.ws.conditional_formatting.add( cell_range, FormulaRule(formula=[condition_formula], fill=self.make_fill(color)) )

使用方式:

formatter = ExcelConditionalFormatter(ws) formatter.add_row_striping(f"A2:E{last_row}", even=True) formatter.add_max_min_highlight("B", 2, last_row) formatter.add_range_by_formula(f"A2:E{last_row}", "$E2>=240", "FFF2CC")

封装之后,主报表脚本会清爽很多,后续扩展指定条件只改一行即可。

10. 最佳实践与工程建议

10.1 优先写出数据,再统一添加条件格式

建议流程:

  1. 先准备数据,完成基础表格;
  2. 统一添加表头、边框、列宽等样式;
  3. 最后再添加条件格式规则。

原因很简单:条件公式中经常需要引用ws.max_row,如果先加规则再写数据,max_row可能只停留在 1,导致规则区域错误。

10.2 将条件格式颜色定义统一常量

条件格式涉及的颜色比较多,例如:

  • 绿底C6EFCE+ 深绿字006100
  • 黄底FFEB9C+ 深黄字9C6500
  • 红底FFC7CE+ 深红字9C0006

这些是 Excel 内置条件格式的经典颜色组合。建议把它们放在文件头部或用单独的constants.py管理,避免在代码中写散落的十六进制值。如果使用方要求品牌色或公司色,后续只需要改一处。

10.3 测试规则时不要随便覆盖原文件

我在实际项目中习惯先将文件保存到output/xxx_demo.xlsx,人工打开验证效果并通过后再决定是否覆盖生产模板。

不要在生产目录下直接执行wb.save(),特别是在 Windows 上文件被 Excel 占用时,写入会报PermissionError。稳妥做法是生成到临时文件再替换。

10.4 文件体积和数据量控制

条件格式规则的数量如果过多,每个规则都应用到一个很大的区域(比如 10 万行),文件打开和运算都会变慢。此时更推荐两种方案:

  1. 把数据量控制到 Excel 合理范围,再添加条件格式;
  2. 使用 Excel 表格(Table)或 Power Query 的思路,在进入 Excel 前先完成聚合。

对于百万行级别的数据,Excel 本来就不是最优展示载体,此时应考虑导出 CSV 或使用 BI 工具。

10.5 理解 Excel 条件格式的“设计时”和“运行时”

条件格式是“设计时生成规则、运行时自动计算”的机制。写 Python 脚本的人必须理解:我们不是在生成一张静态图片,而是在生成一个有智慧的 Excel 模板

因此,交付文档同时要提醒使用者:

  • 不要随意删除条件格式规则;
  • 不要手动给单元格涂色后抱怨颜色不固定;
  • 如果需要扩展数据,建议保留一定空白规则范围,或者先插入 Excel 表格。

10.6 安全与权限提醒

如果你在一个自动化报表流程里,通过 Python 操作公司共享盘上的 Excel 或把生成文件上传到内部系统:

  • 一定要给脚本加配置文件和日志,不要硬编码磁盘路径;
  • 不要把账号口令写在脚本里;
  • 生成文件前确认是否有写入权限;
  • 如果脚本会自动修改线上模板或原始数据文件,务必先备份,并在测试环境验证。

这些看起来和条件格式无关,但真正在团队协作中使用时会成为最容易被忽略的生产事故点。

11. 更多可以继续探索的方向

openpyxl的条件格式功能范围比本文示例更大,官方还支持数据条(DataBar)、图标集(IconSet)、Top10、唯一值/重复值等规则。如果你想继续深入,可以按以下清单逐个练习:

规则类型openpyxl 相关类适用场景
数据条DataBarRule显示业绩长度比较,直观但占空间
图标集IconSetRule用箭头/红绿灯表现升降和健康状态
Top10 规则Top10Rule标记销售前 10 名
唯一值/重复值Rule(type='duplicateValues')清洗数据时快速定位重复记录
包含文本CellIsRule(operator='containsText')对文本列做关键字标记

在实际项目中,我会根据业务场景灵活组合这些规则。比如销售看板用“数据条 + Top10”、考试成绩用“最大值/最小值 + 范围值”、通讯录清洗用“重复值高亮”。

如果你长期处理“Python + Excel”的自动化任务,建议把条件格式纳入你的技能清单,因为它能让交付的 Excel 从“一张数据表”变成“一个自动可视化的报表工具”。哪怕使用者完全不懂公式,也能从颜色中一眼看出问题和高价值数据。

下一步可以做什么?动手把create_report.py里的模拟数据替换成你手头的真实数据,运行后打开 Excel,观察三组核心规则是否达到预期。如果颜色没有出现,优先检查规则区域行号是否正确,然后检查fill_type="solid"是否漏写。只要把这一套跑通,你就能体会到用 Python 控制 Excel 条件格式的效率优势了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询