
本文详解如何利用 openpyxl 的富文本(rich text)功能,仅将单元格中匹配预定义词表的**独立单词**设为红色,避免误匹配子串、重复覆盖或遗漏,并确保多关键词共存时格式正确。
在 Excel 自动化处理中,常需对单元格内特定关键词进行样式强调(如标红),而非整单元格着色。OpenPyXL 从 v3.1+ 开始正式支持 CellRichText,允许对同一单元格内不同字符片段分别设置字体颜色、粗细等属性。但直接遍历替换易引发重叠覆盖、边界错位或单词边界误判(如将 "cat" 匹配到 "category" 中)。以下提供一套健壮、可复用的实现方案。
✅ 核心思路:分段构建富文本
关键在于将原始字符串按所有匹配关键词的位置“切片”,并按顺序拼接黑字(默认)与红字(关键词)片段。为保证准确性,需:
- 使用 \b 单词边界锚点,确保只匹配完整单词;
- 去重并预计算所有匹配区间,按起始位置排序;
- 逐段追加 TextBlock,避免重复覆盖或遗漏尾部文本。
? 完整可运行代码示例
from openpyxl import Workbook
from openpyxl.cell.text import InlineFont
from openpyxl.cell.rich_text import TextBlock, CellRichText
import pandas as pd
import re
# 读取关键词列表(去重 + 转为集合提升查找效率)
prev_words = pd.read_excel("prev_file.xlsx", usecols=['word'])
prev_list = list(set(prev_words['word'].dropna().astype(str))) # 去空、去重、转字符串
# 初始化工作簿与数据
wb = Workbook()
ws = wb.active
df = pd.read_excel("new_file.xlsx")
# 写入 DataFrame(含表头)
for r in dataframe_to_rows(df, index=False, header=True):
ws.append(r)
# 定义字体样式
red_font = InlineFont(color='FF0000') # 红色(简写格式亦可,OpenPyXL 自动补全 alpha)
black_font = InlineFont(color='000000') # 黑色
# 遍历数据行(跳过表头:min_row=2 → 第2行起为数据)
for row in ws.iter_rows(min_row=2):
for cell in row:
if not cell.value: # 跳过空单元格,防止生成 "None" 字符串
continue
msg = str(cell.value)
# 步骤1:收集所有不重叠的匹配区间 [start, end),使用 \b 确保单词边界
red_spans = []
for word in prev_list:
if not word.strip(): # 跳过空白词
continue
# 使用 re.escape 防止关键词含正则元字符(如 *、+、?)
pattern = rf'\b{re.escape(word)}\b'
for match in re.finditer(pattern, msg, re.IGNORECASE): # 忽略大小写更实用
red_spans.append((match.start(), match.end()))
# 步骤2:去重、排序(按起始位置)
red_spans = sorted(set(red_spans))
if not red_spans: # 无匹配,无需处理
continue
# 步骤3:分段构建富文本
rich_text = CellRichText()
prev_end = 0 # 上一个红字片段的结束位置
for start, end in red_spans:
# 添加前一段黑字(从 prev_end 到当前匹配起点)
if prev_end < start:
rich_text.append(TextBlock(black_font, msg[prev_end:start]))
# 添加当前红字
rich_text.append(TextBlock(red_font, msg[start:end]))
prev_end = end
# 添加末尾剩余黑字
if prev_end < len(msg):
rich_text.append(TextBlock(black_font, msg[prev_end:]))
cell.value = rich_text
wb.save("result_highlighted.xlsx")
wb.close()⚠️ 关键注意事项
- 表头跳过:Excel 行号从 1 开始,min_row=2 确保仅处理数据行(若有多级表头,需动态计算 min_row = 1 + df.columns.nlevels)。
- 空值防护:显式检查 if not cell.value,避免 None 转为字符串 "None" 并参与匹配。
- 单词边界 \b:强制匹配完整单词,防止 "cat" 错配 "category" 或 "scatter";配合 re.escape() 可安全处理含特殊字符的关键词(如 "C++"、"a-b")。
- 大小写处理:添加 re.IGNORECASE 标志提升实用性(如 "Dog" 和 "dog" 均可匹配)。
- 性能优化:prev_list 转为 set 去重,red_spans 使用 set() 去重后再排序,避免重复高亮同一位置。
✅ 效果验证
输入单元格内容:
I have a dog, a cat and a fish
关键词列表:["dog", "cat", "fish"]
输出效果:
I have adog, acatand afish
所有关键词独立标红,其余文字保持黑色,无格式丢失、无越界覆盖——真正实现「所见即所得」的精准文本着色。










