Python Word查找并高亮关键词:批量标黄
用python-docx批量在Word文档里查找指定关键词,自动用黄色高亮标注出来,适合合规审查、敏感词扫描、文档校对,结果另存不破坏原文。
场景痛点
法务或合规要在几十份合同里圈出"违约金""不可抗力""保密"这些关键词,人工Ctrl+F一个个看,又慢又漏。用Python扫描整篇文档,把所有命中的词自动标黄高亮,另存一份新文件,人工只需要复核黄色的地方。
用到的库
pip install python-docx
完整代码
# -*- coding: utf-8 -*-
"""
在 Word 文档中查找关键词并高亮(黄色背景),支持批量目录
"""
from pathlib import Path
from docx import Document
from docx.enum.text import WD_COLOR_INDEX
def highlight_keywords_in_paragraph(paragraph, keywords):
"""
把一个段落里命中的关键词高亮。
python-docx 的 run 是格式块,关键词可能跨 run,所以整段重写 runs。
"""
if not paragraph.runs:
return 0
# 拼接整段文本
full_text = paragraph.text
hits = [] # [(start, end), ...]
lower_text = full_text.lower()
for kw in keywords:
kw_lower = kw.lower()
start = 0
while True:
idx = lower_text.find(kw_lower, start)
if idx == -1:
break
hits.append((idx, idx + len(kw)))
start = idx + len(kw)
if not hits:
return 0
# 合并重叠区间
hits.sort()
merged = []
for s, e in hits:
if merged and s <= merged[-1][1]:
merged[-1] = (merged[-1][0], max(merged[-1][1], e))
else:
merged.append((s, e))
# 保留第一个 run 的字体样式,清空所有 run 后重建
base_font = paragraph.runs[0].font
base_name = base_font.name
base_size = base_font.size
base_bold = base_font.bold
base_color = base_font.color.rgb if base_font.color and base_font.color.type else None
# 清空原有 runs
for run in list(paragraph.runs):
run._element.getparent().remove(run._element)
# 按区间重新切分文本并加 run
cursor = 0
for s, e in merged:
if s > cursor:
r = paragraph.add_run(full_text[cursor:s])
_apply_style(r, base_name, base_size, base_bold, base_color)
r_hi = paragraph.add_run(full_text[s:e])
_apply_style(r_hi, base_name, base_size, base_bold, base_color)
r_hi.font.highlight_color = WD_COLOR_INDEX.YELLOW
cursor = e
if cursor < len(full_text):
r = paragraph.add_run(full_text[cursor:])
_apply_style(r, base_name, base_size, base_bold, base_color)
return len(merged)
def _apply_style(run, name, size, bold, color):
if name:
run.font.name = name
# 中文字体需要单独设置 eastAsia
from docx.oxml.ns import qn
run._element.rPr.rFonts.set(qn('w:eastAsia'), name)
if size:
run.font.size = size
if bold is not None:
run.font.bold = bold
if color:
run.font.color.rgb = color
def process_docx(docx_path: Path, output_path: Path, keywords):
doc = Document(str(docx_path))
total_hits = 0
# 段落里找
for para in doc.paragraphs:
total_hits += highlight_keywords_in_paragraph(para, keywords)
# 表格里找
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for para in cell.paragraphs:
total_hits += highlight_keywords_in_paragraph(para, keywords)
doc.save(str(output_path))
return total_hits
def batch_highlight(input_dir, output_dir, keywords):
input_dir = Path(input_dir)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
docx_files = [f for f in input_dir.glob("*.docx") if not f.name.startswith("~$")]
print(f"扫描关键词:{keywords}")
print(f"共 {len(docx_files)} 个文档")
for docx_file in docx_files:
out = output_dir / docx_file.name
hits = process_docx(docx_file, out, keywords)
print(f"✓ {docx_file.name}:命中 {hits} 处 -> {out.name}")
if __name__ == "__main__":
batch_highlight(
input_dir="./word_docs",
output_dir="./highlighted",
keywords=["违约金", "不可抗力", "保密", "知识产权"],
)
代码讲解
python-docx 里一个段落的文字被拆成多个 run,每个 run 有自己的格式。关键词经常跨多个run(比如"违约"在一个run里,"金"在另一个),直接在run里找会漏。所以思路是:先拼接整段文本,找出所有命中区间,再清空原run,按区间重新切分文本、重建run,命中的区间设 WD_COLOR_INDEX.YELLOW 高亮。
合并重叠区间是为了避免两个关键词重合时产生重复run。_apply_style() 把原run的字体、字号、粗体、颜色复制回去,避免重写后格式丢失。中文还需要额外设置 w:eastAsia,否则中文字体变默认。
表格单元格里的段落也遍历一遍,保证表格内的关键词也能被标黄。
运行结果
highlighted 目录里生成同名文档,打开后所有命中关键词的位置都是黄色背景。控制台打印每个文件的命中次数,命中数为0的文档说明没有敏感词,可以跳过复核。
注意事项
- 高亮是Word里的"文本突出显示颜色",不是底纹,颜色只有16种预设,YELLOW/RED/GREEN等够用。
- 本方法会重写段落run,原有的超链接、域代码等复杂元素会丢失,普通合同文档没问题。
- 关键词区分大小写用了
lower(),英文不区分大小写;中文不受影响。 - 想要"替换文字"而不只是高亮,把
r_hi.text替换成新文案即可,注意保持run样式。

更新时间:2026-09-14 21:47:32
下一篇: