Python輕松實(shí)現(xiàn)Word文檔對(duì)比并生成可視化HTML報(bào)告
在日常工作和學(xué)習(xí)中,我們經(jīng)常需要對(duì)兩個(gè)版本的文檔進(jìn)行比對(duì),比如合同修改、論文修訂、報(bào)告更新等。手動(dòng)逐字檢查不僅耗時(shí)費(fèi)力,還容易遺漏細(xì)節(jié)。
今天,我將帶你使用 Python + python-docx + difflib 實(shí)現(xiàn)一個(gè)自動(dòng)化 Word 文檔對(duì)比工具,不僅能精準(zhǔn)識(shí)別段落增刪改,還能生成美觀的 HTML 可視化報(bào)告,并自動(dòng)在瀏覽器中打開查看!
項(xiàng)目效果預(yù)覽
運(yùn)行腳本后:
- 自動(dòng)讀取兩個(gè)
.docx文件 - 按段落逐一對(duì)比內(nèi)容差異
- 生成一份 高亮標(biāo)注差異 的 HTML 報(bào)告
- 支持瀏覽器自動(dòng)打開,無需手動(dòng)查找文件
完全自動(dòng)化 | 差異高亮顯示 | 支持中文 | 界面美觀專業(yè)
技術(shù)棧簡介
| 技術(shù) | 作用 |
|---|---|
| python-docx | 讀取 .docx 文件內(nèi)容 |
| difflib | 計(jì)算文本差異(增刪改) |
| webbrowser | 自動(dòng)打開瀏覽器預(yù)覽結(jié)果 |
| os / datetime | 文件路徑處理與時(shí)間戳記錄 |
| 原生 HTML + CSS | 生成結(jié)構(gòu)清晰、樣式現(xiàn)代化的報(bào)告頁面 |
核心功能解析
讀取.docx文件內(nèi)容
from docx import Document
def read_docx(file_path):
"""讀取 .docx 文件,返回段落列表"""
try:
doc = Document(file_path)
paragraphs = [para.text.strip() for para in doc.paragraphs if para.text.strip()]
return paragraphs
except Exception as e:
print(f"? 讀取文件 {file_path} 時(shí)出錯(cuò): {e}")
return []
說明:
- 使用
Document加載 Word 文件 - 提取所有非空段落,并去除首尾空格
- 異常捕獲確保程序健壯性
利用difflib實(shí)現(xiàn)文本差異分析
matcher = difflib.SequenceMatcher(None, p1, p2) opcodes = matcher.get_opcodes()
difflib.SequenceMatcher 是 Python 內(nèi)置的強(qiáng)大工具,可以分析兩個(gè)字符串之間的差異操作(equal, insert, delete, replace),我們據(jù)此為每個(gè)字符添加 HTML 標(biāo)簽高亮。
例如:
<span class='diff-delete'>[刪除: 這句話被刪了]</span> <span class='diff-add'>[新增: 這是新內(nèi)容]</span>
生成精美 HTML 報(bào)告
我們構(gòu)建了一個(gè)完整的 HTML 頁面,包含:
響應(yīng)式設(shè)計(jì) + 現(xiàn)代化 UI 風(fēng)格
- 漸變標(biāo)題欄
- 圓角卡片式表格
- 懸停交互效果
- 適配中文字體
對(duì)比概覽區(qū)
顯示文件名、段落數(shù)量、生成時(shí)間等元信息。
詳細(xì)對(duì)比表格
| 段落 | 文檔1內(nèi)容 | 文檔2內(nèi)容 | 差異狀態(tài) |
|---|---|---|---|
| 第1段 | 相同 | 相同 | 完全相同 |
| 第2段 | 被刪除 | 新增內(nèi)容 | 內(nèi)容有差異 |
每一段都用顏色區(qū)分:
- 綠色:相同內(nèi)容
- 紅色:刪除或缺失
- 藍(lán)色:新增內(nèi)容
- 黃色背景:整段新增/刪除
自動(dòng)生成并打開報(bào)告
abs_path = os.path.abspath(report_path)
url = f"file://{abs_path}"
webbrowser.open(url)
一鍵生成報(bào)告后,自動(dòng)調(diào)用系統(tǒng)默認(rèn)瀏覽器打開,用戶體驗(yàn)極佳!
使用方法(超簡單)
步驟 1:安裝依賴
pip install python-docx
注意:difflib 是標(biāo)準(zhǔn)庫,無需安裝。
步驟 2:修改主函數(shù)中的文件路徑
file1_path = r'C:\Users\Administrator\Desktop\11.docx' file2_path = r'C:\Users\Administrator\Desktop\22docx.docx' file1_name = "11.docx (原文版)" file2_name = "22docx.docx (對(duì)比版)" output_html_file = "荷塘月色_對(duì)比報(bào)告.html"
支持任意 .docx 文件,建議使用絕對(duì)路徑避免出錯(cuò)。
步驟 3:運(yùn)行腳本
python docx_compare.py
輸出示例:
?? 正在啟動(dòng)文檔對(duì)比程序...
============================================================
? 成功讀取文檔!
• 11.docx (原文版) 共 15 段
• 22docx.docx (對(duì)比版) 共 17 段
? HTML 報(bào)告已成功生成: '荷塘月色_對(duì)比報(bào)告.html'
?? 報(bào)告已自動(dòng)在默認(rèn)瀏覽器中打開。
報(bào)告截圖展示


- 頂部標(biāo)題:帶有時(shí)間戳和漸變背景
- 對(duì)比表格:左右并列展示,差異高亮
- 狀態(tài)標(biāo)識(shí):圖標(biāo)+顏色提示,一目了然
完整代碼下載
你可以將本文提供的完整代碼保存為 docx_compare.py,稍作配置即可使用。
import difflib
from docx import Document
import webbrowser
import os
from datetime import datetime
def read_docx(file_path):
"""讀取 .docx 文件,返回段落列表"""
try:
doc = Document(file_path)
paragraphs = [para.text.strip() for para in doc.paragraphs if para.text.strip()]
return paragraphs
except Exception as e:
print(f"? 讀取文件 {file_path} 時(shí)出錯(cuò): {e}")
return []
def generate_html_report(paras1, paras2, file1_name, file2_name, output_html="對(duì)比報(bào)告.html"):
"""
根據(jù)對(duì)比結(jié)果生成 HTML 報(bào)告。
"""
# 構(gòu)建 HTML 內(nèi)容
html_content = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文檔對(duì)比報(bào)告</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.8;
color: #333;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f9f9f9;
}}
.header {{
text-align: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px 20px;
border-radius: 10px;
margin-bottom: 30px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}}
.header h1 {{
margin: 0;
font-size: 2.5em;
}}
.header p {{
margin: 10px 0 0;
opacity: 0.9;
}}
.comparison-table {{
width: 100%;
border-collapse: collapse;
margin: 20px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
background-color: white;
border-radius: 8px;
overflow: hidden;
}}
.comparison-table th {{
background-color: #4a5568;
color: white;
text-align: left;
padding: 15px;
font-weight: 600;
}}
.comparison-table td {{
padding: 15px;
border-bottom: 1px solid #e2e8f0;
vertical-align: top;
}}
.comparison-table tr:nth-child(even) {{
background-color: #f7fafc;
}}
.comparison-table tr:hover {{
background-color: #ebf8ff;
}}
.diff-equal {{
color: #22c55e;
font-weight: 600;
}}
.diff-delete {{
color: #e53e3e;
font-weight: 600;
}}
.diff-add {{
color: #3182ce;
font-weight: 600;
}}
.context {{
font-family: 'Courier New', monospace;
background-color: #f1f5f9;
padding: 10px;
border-radius: 5px;
white-space: pre-wrap;
word-wrap: break-word;
font-size: 0.95em;
}}
.footer {{
text-align: center;
margin-top: 40px;
color: #718096;
font-size: 0.9em;
}}
.highlight {{
background-color: #fff3cd;
padding: 2px 4px;
border-radius: 3px;
}}
</style>
</head>
<body>
<div class="header">
<h1>?? 文檔對(duì)比報(bào)告</h1>
<p>生成時(shí)間: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
</div>
<h2>?? 對(duì)比概覽</h2>
<p><strong>文檔 1:</strong> {file1_name}</p>
<p><strong>文檔 2:</strong> {file2_name}</p>
<p><strong>段落數(shù)量:</strong> 文檔1: {len(paras1)} 段, 文檔2: {len(paras2)} 段</p>
<h2>?? 詳細(xì)對(duì)比結(jié)果</h2>
<table class="comparison-table">
<thead>
<tr>
<th>段落</th>
<th>{file1_name}</th>
<th>{file2_name}</th>
<th>差異狀態(tài)</th>
</tr>
</thead>
<tbody>
"""
max_len = max(len(paras1), len(paras2))
for i in range(max_len):
p1 = paras1[i] if i < len(paras1) else None
p2 = paras2[i] if i < len(paras2) else None
# 生成差異標(biāo)記文本
if p1 is not None and p2 is not None:
matcher = difflib.SequenceMatcher(None, p1, p2)
opcodes = matcher.get_opcodes()
result1 = []
result2 = []
for tag, i1, i2, j1, j2 in opcodes:
if tag == 'equal':
result1.append(p1[i1:i2])
result2.append(p2[j1:j2])
elif tag == 'delete':
result1.append(f"<span class='diff-delete'>[刪除: {p1[i1:i2]}]</span>")
result2.append(f"<span class='diff-add'>[缺失]</span>")
elif tag == 'insert':
result1.append(f"<span class='diff-add'>[缺失]</span>")
result2.append(f"<span class='diff-delete'>[新增: {p2[j1:j2]}]</span>")
elif tag == 'replace':
result1.append(f"<span class='diff-delete'>[刪除: {p1[i1:i2]}]</span>")
result2.append(f"<span class='diff-add'>[新增: {p2[j1:j2]}]</span>")
marked_p1 = ''.join(result1)
marked_p2 = ''.join(result2)
else:
marked_p1 = p1 or "<span class='diff-add'>[該段落不存在]</span>"
marked_p2 = p2 or "<span class='diff-add'>[該段落不存在]</span>"
# 確定差異狀態(tài)
if p1 is None:
status = f"<span class='diff-add'>? 新增段落</span>"
elif p2 is None:
status = f"<span class='diff-delete'>? 刪除段落</span>"
elif p1 == p2:
status = f"<span class='diff-equal'>? 完全相同</span>"
else:
status = f"<span class='diff-delete'>?? 內(nèi)容有差異</span>"
# 添加到HTML表格中
paragraph_num = f"第 {i+1} 段"
html_content += f"""
<tr>
<td><strong>{paragraph_num}</strong></td>
<td class="context">{marked_p1}</td>
<td class="context">{marked_p2}</td>
<td>{status}</td>
</tr>
"""
# 閉合HTML標(biāo)簽
html_content += f"""
</tbody>
</table>
<div class="footer">
<p>此報(bào)告由 Python 文檔對(duì)比工具自動(dòng)生成</p>
</div>
</body>
</html>
"""
# 寫入文件
try:
with open(output_html, 'w', encoding='utf-8') as f:
f.write(html_content)
print(f"? HTML 報(bào)告已成功生成: '{output_html}'")
return output_html
except Exception as e:
print(f"? 生成 HTML 文件時(shí)出錯(cuò): {e}")
return None
def main():
# ?? 請(qǐng)修改為您的實(shí)際文件路徑
file1_path = r'C:\Users\Administrator\Desktop\11.docx'
file2_path = r'C:\Users\Administrator\Desktop\22docx.docx'
# 自定義顯示名稱
file1_name = "11.docx (原文版)"
file2_name = "22docx.docx (對(duì)比版)"
output_html_file = "荷塘月色_對(duì)比報(bào)告.html"
print("?? 正在啟動(dòng)文檔對(duì)比程序...")
print("=" * 60)
# 讀取文檔
paras1 = read_docx(file1_path)
paras2 = read_docx(file2_path)
if not paras1 or not paras2:
print("? 文檔讀取失敗,請(qǐng)檢查文件。")
return
print(f"? 成功讀取文檔!")
print(f" ? {file1_name} 共 {len(paras1)} 段")
print(f" ? {file2_name} 共 {len(paras2)} 段")
# 生成 HTML 報(bào)告
report_path = generate_html_report(paras1, paras2, file1_name, file2_name, output_html_file)
if report_path:
# 獲取文件的絕對(duì)路徑
abs_path = os.path.abspath(report_path)
# 構(gòu)造 file:// URL
url = f"file://{abs_path}"
# 自動(dòng)打開瀏覽器
try:
webbrowser.open(url)
print(f"?? 報(bào)告已自動(dòng)在默認(rèn)瀏覽器中打開。")
except Exception as e:
print(f"?? 自動(dòng)打開瀏覽器失敗,您可以手動(dòng)打開文件:\n {abs_path}")
else:
print("? 報(bào)告生成失敗。")
if __name__ == "__main__":
main()
以上就是Python輕松實(shí)現(xiàn)Word文檔對(duì)比并生成可視化HTML報(bào)告的詳細(xì)內(nèi)容,更多關(guān)于Python對(duì)比Word文檔的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python Pandas describe()函數(shù)的使用詳解
pandas庫中的describe()函數(shù)為我們提供了這樣的功能,它可以快速生成數(shù)據(jù)集的描述性統(tǒng)計(jì)信息,這篇文章主要介紹了Python Pandas describe()函數(shù)的使用介紹,需要的朋友可以參考下2024-05-05
快速解決pyqt5窗體關(guān)閉后子線程不同時(shí)退出的問題
今天小編就為大家分享一篇快速解決pyqt5窗體關(guān)閉后子線程不同時(shí)退出的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-06-06
Pycharm中切換pytorch的環(huán)境和配置的教程詳解
這篇文章主要介紹了Pycharm中切換pytorch的環(huán)境和配置,本文給大家介紹的非常詳細(xì),對(duì)大家的工作或?qū)W習(xí)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
Pandas中八個(gè)常用option設(shè)置的示例詳解
通過pandas的使用,我們經(jīng)常要交互式地展示表格(dataframe)、分析表格。本文整理了8個(gè)常用的配置選項(xiàng),使用可以提高很多效率,需要的可以參考一下2022-06-06
python執(zhí)行數(shù)據(jù)庫的查詢操作實(shí)例講解
在本篇文章里小編給大家整理了一篇關(guān)于python執(zhí)行數(shù)據(jù)庫的查詢操作實(shí)例講解內(nèi)容,有需要的朋友們可以參考學(xué)習(xí)下。2021-10-10
Python實(shí)現(xiàn)基于HTTP文件傳輸實(shí)例
這篇文章主要介紹了Python實(shí)現(xiàn)基于HTTP文件傳輸?shù)姆椒?以實(shí)例形式詳細(xì)講述了server端與client端的實(shí)現(xiàn)代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-11-11
Python利用matplotlib模塊數(shù)據(jù)可視化繪制3D圖
matplotlib是python最著名的繪圖庫,它提供了一整套和matlab相似的命令A(yù)PI,十分適合交互式地行制圖,下面這篇文章主要給大家介紹了關(guān)于Python利用matplotlib模塊數(shù)據(jù)可視化實(shí)現(xiàn)3D圖的相關(guān)資料,需要的朋友可以參考下2022-02-02
Python調(diào)用接口合并Excel表代碼實(shí)例
這篇文章主要介紹了Python調(diào)用接口合并Excel表代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03

