在日常工作和學習中,我們經常需要對兩個版本的文檔進行比對,比如合同修改、論文修訂、報告更新等。手動逐字檢查不僅耗時費力,還容易遺漏細節。
今天,我將帶你使用 Python + python-docx
+ difflib
實現一個自動化 Word 文檔對比工具,不僅能精準識別段落增刪改,還能生成美觀的 HTML 可視化報告,并自動在瀏覽器中打開查看!
🚀 項目效果預覽
運行腳本后:
- 自動讀取兩個
.docx
文件 - 按段落逐一對比內容差異
- 生成一份 高亮標注差異 的 HTML 報告
- 支持瀏覽器自動打開,無需手動查找文件
? 完全自動化 | ? 差異高亮顯示 | ? 支持中文 | ? 界面美觀專業
🛠? 技術棧簡介
技術 | 作用 |
---|---|
python-docx | 讀取 .docx 文件內容 |
difflib | 計算文本差異(增刪改) |
webbrowser | 自動打開瀏覽器預覽結果 |
os / datetime | 文件路徑處理與時間戳記錄 |
原生 HTML + CSS | 生成結構清晰、樣式現代化的報告頁面 |
📂 核心功能解析
1?? 讀取 .docx
文件內容
from docx import Documentdef read_docx(file_path):"""讀取 .docx 文件,返回段落列表"""try:doc = Document(file_path)paragraphs = [para.text.strip() for para in doc.paragraphs if para.text.strip()]return paragraphsexcept Exception as e:print(f"? 讀取文件 {file_path} 時出錯: {e}")return []
📌 說明:
- 使用
Document
加載 Word 文件 - 提取所有非空段落,并去除首尾空格
- 異常捕獲確保程序健壯性
2?? 利用 difflib
實現文本差異分析
matcher = difflib.SequenceMatcher(None, p1, p2)
opcodes = matcher.get_opcodes()
difflib.SequenceMatcher
是 Python 內置的強大工具,可以分析兩個字符串之間的差異操作(equal, insert, delete, replace),我們據此為每個字符添加 HTML 標簽高亮。
例如:
<span class='diff-delete'>[刪除: 這句話被刪了]</span>
<span class='diff-add'>[新增: 這是新內容]</span>
3?? 生成精美 HTML 報告
我們構建了一個完整的 HTML 頁面,包含:
? 響應式設計 + 現代化 UI 風格
- 漸變標題欄
- 圓角卡片式表格
- 懸停交互效果
- 適配中文字體
? 對比概覽區
顯示文件名、段落數量、生成時間等元信息。
? 詳細對比表格
段落 | 文檔1內容 | 文檔2內容 | 差異狀態 |
---|---|---|---|
第1段 | ? 相同 | ? 相同 | ? 完全相同 |
第2段 | ? 被刪除 | ? 新增內容 | ?? 內容有差異 |
每一段都用顏色區分:
- 🟢 綠色:相同內容
- 🔴 紅色:刪除或缺失
- 🔵 藍色:新增內容
- 🟡 黃色背景:整段新增/刪除
4?? 自動生成并打開報告
abs_path = os.path.abspath(report_path)
url = f"file://{abs_path}"
webbrowser.open(url)
一鍵生成報告后,自動調用系統默認瀏覽器打開,用戶體驗極佳!
💡 使用方法(超簡單)
步驟 1:安裝依賴
pip install python-docx
?? 注意:
difflib
是標準庫,無需安裝。
步驟 2:修改主函數中的文件路徑
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 (對比版)"
output_html_file = "荷塘月色_對比報告.html"
📌 支持任意 .docx
文件,建議使用絕對路徑避免出錯。
步驟 3:運行腳本
python docx_compare.py
輸出示例:
🚀 正在啟動文檔對比程序...
============================================================
? 成功讀取文檔!? 11.docx (原文版) 共 15 段? 22docx.docx (對比版) 共 17 段
? HTML 報告已成功生成: '荷塘月色_對比報告.html'
🌐 報告已自動在默認瀏覽器中打開。
🖼? 報告截圖展示
- 頂部標題:帶有時間戳和漸變背景
- 對比表格:左右并列展示,差異高亮
- 狀態標識:圖標+顏色提示,一目了然
📎 完整代碼下載
你可以將本文提供的完整代碼保存為 docx_compare.py
,稍作配置即可使用。
import difflib
from docx import Document
import webbrowser
import os
from datetime import datetimedef read_docx(file_path):"""讀取 .docx 文件,返回段落列表"""try:doc = Document(file_path)paragraphs = [para.text.strip() for para in doc.paragraphs if para.text.strip()]return paragraphsexcept Exception as e:print(f"? 讀取文件 {file_path} 時出錯: {e}")return []def generate_html_report(paras1, paras2, file1_name, file2_name, output_html="對比報告.html"):"""根據對比結果生成 HTML 報告。"""# 構建 HTML 內容html_content = f"""<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>文檔對比報告</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>📄 文檔對比報告</h1><p>生成時間: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p></div><h2>📊 對比概覽</h2><p><strong>文檔 1:</strong> {file1_name}</p><p><strong>文檔 2:</strong> {file2_name}</p><p><strong>段落數量:</strong> 文檔1: {len(paras1)} 段, 文檔2: {len(paras2)} 段</p><h2>🔍 詳細對比結果</h2><table class="comparison-table"><thead><tr><th>段落</th><th>{file1_name}</th><th>{file2_name}</th><th>差異狀態</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 Nonep2 = paras2[i] if i < len(paras2) else None# 生成差異標記文本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>"# 確定差異狀態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'>?? 內容有差異</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標簽html_content += f"""</tbody></table><div class="footer"><p>此報告由 Python 文檔對比工具自動生成</p></div></body></html>"""# 寫入文件try:with open(output_html, 'w', encoding='utf-8') as f:f.write(html_content)print(f"? HTML 報告已成功生成: '{output_html}'")return output_htmlexcept Exception as e:print(f"? 生成 HTML 文件時出錯: {e}")return Nonedef main():# 🔧 請修改為您的實際文件路徑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 (對比版)"output_html_file = "荷塘月色_對比報告.html"print("🚀 正在啟動文檔對比程序...")print("=" * 60)# 讀取文檔paras1 = read_docx(file1_path)paras2 = read_docx(file2_path)if not paras1 or not paras2:print("? 文檔讀取失敗,請檢查文件。")returnprint(f"? 成功讀取文檔!")print(f" ? {file1_name} 共 {len(paras1)} 段")print(f" ? {file2_name} 共 {len(paras2)} 段")# 生成 HTML 報告report_path = generate_html_report(paras1, paras2, file1_name, file2_name, output_html_file)if report_path:# 獲取文件的絕對路徑abs_path = os.path.abspath(report_path)# 構造 file:// URLurl = f"file://{abs_path}"# 自動打開瀏覽器try:webbrowser.open(url)print(f"🌐 報告已自動在默認瀏覽器中打開。")except Exception as e:print(f"?? 自動打開瀏覽器失敗,您可以手動打開文件:\n {abs_path}")else:print("? 報告生成失敗。")if __name__ == "__main__":main()