Python實現(xiàn)word文檔內容智能提取以及合成
如何從10個左右的docx文檔中抽取內容,生成新的文檔,抽取內容包括源文檔的文字內容、圖片、表格、公式等,以及目標文檔的樣式排版、字體、格式,還有目標文檔的語言風格、用詞規(guī)范、文法習慣等等。這是一個相當復雜的需求,因為它不僅涉及內容提取,還涉及深度格式化和風格模仿。完全自動化的完美解決方案難度極高,特別是對于復雜的公式和微妙的語言風格。
一個務實的方案是采用 自動化 + 人工輔助 的混合策略。以下是詳細的思路、技術路徑、方法和步驟:
核心思路
內容提取 (自動化為主): 使用編程方式從源 DOCX 文件中提取所需的核心內容(文字、圖片、表格、公式的某種表示)。
樣式應用 (自動化): 基于一個定義了目標樣式、排版、字體等的 模板文檔,將提取的內容插入新文檔,并應用模板中定義的樣式。
語言風格調整 (自動化輔助 + 人工): 利用大型語言模型 (LLM) 或自然語言處理 (NLP) 技術對提取的文本進行初步的風格、用詞和文法調整,然后進行人工審閱和精修。
復雜元素處理 (人工為主): 對于難以自動處理的元素(如復雜公式、特定排版),進行人工調整。
技術路徑
主要工具: Python 編程語言
核心庫:
- python-docx: 用于讀取和寫入 DOCX 文件(文本、表格、圖片、基本樣式應用)。
- (可選) 用于公式處理: 可能需要解析 DOCX 的底層 XML (OOXML),或者尋找專門處理 MathML/OMML 的庫(這部分比較困難),或者將公式提取為圖片。
- (可選) 用于圖片處理: Pillow (PIL Fork) 可能需要用于處理圖片。
- (可選) 用于語言風格調整: 調用大型語言模型 API (如 OpenAI GPT 系列、Google Gemini、或其他類似服務)。
輔助工具:
- Microsoft Word: 用于創(chuàng)建模板文檔、最終審閱和調整。
- XML 編輯器 (可選): 用于深入分析 DOCX 內部結構(特別是公式)。
實現(xiàn)步驟
階段一:準備工作
1.創(chuàng)建目標模板文檔 (template.docx):
- 在 Word 中創(chuàng)建一個新文檔。
- 定義樣式: 精心設置所有需要的樣式(標題 1、標題 2、正文、引用、列表、表格樣式等),包括字體、字號、顏色、段落間距、縮進等。確保樣式名稱清晰易懂(例如 TargetHeading1, TargetBodyText, TargetTableStyle)。
- 設置頁面布局: 頁邊距、紙張大小、頁眉頁腳等。
- 保存: 將此文檔保存為 template.docx。這將是所有新生成文檔的基礎。
2.明確提取規(guī)則:
關鍵: 你需要非常清楚地定義 哪些 內容需要從每個源文檔中提取出來。規(guī)則可以基于:
- 特定標題: “提取 ‘第三章 方法’ 下的所有內容”。
- 特定樣式: “提取所有應用了 ‘源文檔重點’ 樣式的內容”。
- 關鍵詞/標記: “提取包含 ‘[EXTRACT]’ 標記的段落”。
- 結構位置: “提取每個文檔的第二個表格”。
- 人工指定: (最靈活但最慢) 手動在源文檔中標記要提取的內容(例如使用 Word 的批注功能或特定高亮顏色),然后讓腳本識別這些標記。
- 文檔化規(guī)則: 將這些規(guī)則清晰地記錄下來,以便編寫腳本。
3.設置開發(fā)環(huán)境:
安裝 Python。
使用 pip 安裝必要的庫:
pip install python-docx Pillow requests # 如果需要調用 LLM API # 可能需要其他庫,取決于具體實現(xiàn)
(可選) 獲取 LLM API 密鑰。
階段二:內容提取 (Python 腳本)
import os from docx import Document from docx.shared import Inches # 可能需要導入其他模塊,如處理 XML 或調用 API # --- 配置 --- SOURCE_DOCS_DIR = 'files/transform/docx/source_documents' TARGET_TEMPLATE = 'files/transform/docx/template.docx' OUTPUT_DOC_PATH = 'files/transform/docx/generated_document.docx' EXTRACTION_RULES = { # 示例規(guī)則,需要根據(jù)你的實際情況修改 'source_doc_1.docx': {'heading_start': 'Chapter 3', 'heading_end': 'Chapter 4'}, 'source_doc_2.docx': {'style_name': 'SourceHighlight'}, # ... 其他文檔的規(guī)則 } # --- 輔助函數(shù) (示例) --- def should_extract_paragraph(paragraph, rules): # 實現(xiàn)基于規(guī)則判斷段落是否應該提取的邏輯 # 例如:檢查段落文本是否匹配、樣式是否匹配等 # 返回 True 或 False # (這部分邏輯需要根據(jù)你的具體規(guī)則編寫) style_name = paragraph.style.name text = paragraph.text.strip() # 示例:基于樣式的簡單規(guī)則 if 'style_name' in rules and style_name == rules['style_name']: return True # 示例:基于起始標題的簡單規(guī)則(需要狀態(tài)管理) # if 'heading_start' in rules ... (需要更復雜的邏輯來跟蹤當前章節(jié)) return False # 默認不提取 def extract_content_from_doc(source_path, rules): """從單個源文檔提取內容""" extracted_elements = [] try: source_doc = Document(source_path) # 標記是否處于提取區(qū)域(例如,在特定章節(jié)之間) in_extraction_zone = False # 需要根據(jù)規(guī)則調整初始狀態(tài) for element in source_doc.element.body: # 處理不同類型的元素:段落、表格等 if element.tag.endswith('p'): # 是段落 paragraph = docx.text.paragraph.Paragraph(element, source_doc) # --- 核心提取邏輯 --- # 這里需要根據(jù)你的 EXTRACTION_RULES 實現(xiàn)復雜的判斷邏輯 # 例如,判斷是否遇到起始標題,是否遇到結束標題,段落樣式是否匹配等 # 這是一個簡化的示例,實際可能需要更精細的狀態(tài)管理 if 'heading_start' in rules and paragraph.style.name.startswith('Heading') and rules['heading_start'] in paragraph.text: in_extraction_zone = True continue # 不提取起始標題本身?看需求 if 'heading_end' in rules and paragraph.style.name.startswith('Heading') and rules['heading_end'] in paragraph.text: in_extraction_zone = False continue # 到達結束標題,停止提取 if in_extraction_zone or should_extract_paragraph(paragraph, rules): # 提取文本內容 text_content = paragraph.text # 嘗試提取基本格式(粗體、斜體) - 比較復雜,可能需要遍歷 runs # TODO: 提取圖片 (需要檢查段落中的 inline_shapes 或 runs 中的 drawing) # TODO: 提取公式 (極具挑戰(zhàn)性,見下文討論) extracted_elements.append({'type': 'paragraph', 'text': text_content, 'style': paragraph.style.name}) # 可以攜帶源樣式名供參考 elif element.tag.endswith('tbl'): # 是表格 table = docx.table.Table(element, source_doc) # --- 提取表格 --- # TODO: 實現(xiàn)表格提取邏輯,可能需要檢查是否在提取區(qū)域內 # if in_extraction_zone or table_should_be_extracted(table, rules): table_data = [] for row in table.rows: row_data = [cell.text for cell in row.cells] table_data.append(row_data) extracted_elements.append({'type': 'table', 'data': table_data}) # --- 處理圖片 --- # 查找段落內的圖片 (inline_shapes) # paragraph = docx.text.paragraph.Paragraph(element, source_doc) # Re-get paragraph object if needed # for run in paragraph.runs: # if run.element.xpath('.//wp:inline | .//wp:anchor'): # Check for drawings # # This part is complex: need to get image data (rId) and relate it back # # to the actual image part in the docx package. # # python-docx can extract images, but associating them perfectly # # with their original position during extraction requires care. # # Placeholder: # # image_data = get_image_data(run, source_doc) # # if image_data: # # extracted_elements.append({'type': 'image', 'data': image_data, 'filename': f'img_{len(extracted_elements)}.png'}) pass # Placeholder for image extraction logic except Exception as e: print(f"Error processing {source_path}: {e}") return extracted_elements # --- 主流程 --- all_extracted_content = [] source_files = [f for f in os.listdir(SOURCE_DOCS_DIR) if f.endswith('.docx')] for filename in source_files: source_path = os.path.join(SOURCE_DOCS_DIR, filename) rules = EXTRACTION_RULES.get(filename, {}) # 獲取該文件的提取規(guī)則 if rules: # 只處理定義了規(guī)則的文件 print(f"Extracting from: {filename}") content = extract_content_from_doc(source_path, rules) all_extracted_content.extend(content) else: print(f"Skipping {filename}, no rules defined.") print(f"Total elements extracted: {len(all_extracted_content)}")
階段三:語言風格調整 (可選, Python + LLM API)
# --- --- import requests import json # --- 配置 LLM --- LLM_API_URL = "YOUR_LLM_API_ENDPOINT" # e.g., OpenAI API URL LLM_API_KEY = "YOUR_LLM_API_KEY" LLM_PROMPT_TEMPLATE = """ 請根據(jù)以下要求,改寫這段文字: 目標語言風格:[在此處詳細描述,例如:正式、客觀、簡潔] 用詞規(guī)范:[在此處列出規(guī)范,例如:使用“用戶”而非“客戶”,避免使用縮寫] 文法習慣:[在此處描述,例如:多使用主動語態(tài),句子長度適中] 目標受眾:[描述目標讀者] 原文: "{text}" 改寫后的文字: """ def adapt_text_style(text): """使用 LLM API 調整文本風格""" if not text.strip(): return text # 跳過空文本 prompt = LLM_PROMPT_TEMPLATE.format(text=text) headers = { "Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json", } data = { "model": "gpt-4", # 或你使用的模型 "prompt": prompt, "max_tokens": 1024, # 根據(jù)需要調整 "temperature": 0.5, # 控制創(chuàng)造性,較低值更保守 } try: response = requests.post(LLM_API_URL, headers=headers, json=data) response.raise_for_status() # 檢查 HTTP 錯誤 result = response.json() # 解析 LLM 返回的結果,注意不同 API 的格式可能不同 rewritten_text = result['choices'][0]['text'].strip() # 示例路徑 print(f"Original: {text[:50]}... | Rewritten: {rewritten_text[:50]}...") return rewritten_text except requests.exceptions.RequestException as e: print(f"Error calling LLM API: {e}") return text # 出錯時返回原文 except (KeyError, IndexError) as e: print(f"Error parsing LLM response: {e} - Response: {response.text}") return text # 出錯時返回原文 # --- 應用風格調整 --- adjusted_content = [] for element in all_extracted_content: if element['type'] == 'paragraph': # --- 調用 LLM API --- # adjusted_text = adapt_text_style(element['text']) # element['text'] = adjusted_text # 更新文本 # --- 或者先不調用,等生成后再處理 --- adjusted_content.append(element) elif element['type'] == 'table': # 表格內容也可以逐個單元格處理,但可能效果不佳或成本高 # 更好的方法可能是將表格內容整理成文本描述給 LLM,或者人工處理 adjusted_content.append(element) elif element['type'] == 'image': # 圖片無法直接處理 adjusted_content.append(element) # 處理其他類型... # --- (接續(xù)到下一階段:文檔生成) ---
階段四:生成目標文檔 (Python 腳本)
# --- (續(xù)上) --- # --- 創(chuàng)建目標文檔 (基于模板) --- try: target_doc = Document(TARGET_TEMPLATE) except Exception as e: print(f"Error loading template {TARGET_TEMPLATE}: {e}") # 可以考慮創(chuàng)建一個空文檔作為后備 # target_doc = Document() exit() # --- 填充內容并應用樣式 --- for element in adjusted_content: # 使用調整后的內容,或者原始提取內容 if element['type'] == 'paragraph': text = element['text'] # --- 核心:應用模板中定義的樣式 --- # 簡單方式:所有段落應用默認正文樣式 # target_doc.add_paragraph(text, style='TargetBodyText') # 假設模板中有此樣式 # 復雜方式:根據(jù)源文檔信息或內容判斷應用哪個目標樣式 # 示例:如果源樣式是 Heading 1,應用 TargetHeading1 source_style = element.get('style', '') # 獲取源樣式名(如果提取時保存了) if source_style.startswith('Heading 1'): target_doc.add_paragraph(text, style='TargetHeading1') # 假設模板中有此樣式 elif source_style.startswith('Heading 2'): target_doc.add_paragraph(text, style='TargetHeading2') # ... 其他樣式映射規(guī)則 else: target_doc.add_paragraph(text, style='TargetBodyText') # 默認樣式 elif element['type'] == 'table': table_data = element['data'] if table_data: # 創(chuàng)建表格 num_rows = len(table_data) num_cols = len(table_data[0]) if num_rows > 0 else 0 if num_rows > 0 and num_cols > 0: # --- 應用模板中定義的表格樣式 --- table = target_doc.add_table(rows=num_rows, cols=num_cols, style='TargetTableStyle') # 假設模板中有此表格樣式 # 填充數(shù)據(jù) for i, row_data in enumerate(table_data): for j, cell_text in enumerate(row_data): # 防止列數(shù)不匹配錯誤 if j < len(table.rows[i].cells): table.rows[i].cells[j].text = cell_text # 可以添加更多表格格式化代碼,如設置列寬等 elif element['type'] == 'image': # --- 添加圖片 --- # image_data = element['data'] # image_filename = element['filename'] # # 需要將 image_data 保存為臨時文件或使用 BytesIO # from io import BytesIO # image_stream = BytesIO(image_data) # try: # target_doc.add_picture(image_stream, width=Inches(4.0)) # 調整寬度 # except Exception as e: # print(f"Error adding image {image_filename}: {e}") pass # Placeholder for image insertion # --- 處理公式 (挑戰(zhàn)) --- # 如果公式被提取為圖片: # elif element['type'] == 'formula_image': # # 添加圖片... # 如果公式被提取為 MathML/OMML (XML 字符串): # elif element['type'] == 'formula_mathml': # # 使用 python-docx 直接插入 MathML 很困難 # # 可能需要直接操作 OOXML (非常復雜) # # 或者,在段落中插入一個占位符 "[FORMULA]",然后手動替換 # target_doc.add_paragraph(f"[FORMULA: {element['id']}]", style='TargetBodyText') # 如果公式被提取為純文本近似值: # elif element['type'] == 'formula_text': # target_doc.add_paragraph(element['text'], style='FormulaStyle') # 可能需要特殊樣式 # --- 保存最終文檔 --- try: target_doc.save(OUTPUT_DOC_PATH) print(f"Document successfully generated: {OUTPUT_DOC_PATH}") except Exception as e: print(f"Error saving document: {e}")
階段五:人工審閱與精修
1.打開生成的文檔 (generated_document.docx)。
2.檢查整體結構和內容完整性: 是否所有需要的內容都被提取并放置在正確的位置?
3.檢查樣式和格式:
- 所有文本是否應用了正確的模板樣式?
- 字體、字號、間距是否符合要求?
- 表格樣式是否正確?列寬、對齊是否需要調整?
- 圖片位置和大小是否合適?
4.檢查語言風格和規(guī)范:
- 通讀文本,檢查語氣、用詞是否符合目標要求。
- 修正 LLM 可能產生的錯誤或不自然的表達。
- 確保術語統(tǒng)一。
- 進行拼寫和語法檢查。
5.處理復雜元素:
公式: 這是最可能需要手動操作的地方。如果腳本插入了占位符,你需要手動將源文檔中的公式復制粘貼過來,或者使用 Word 的公式編輯器重新創(chuàng)建它們。確保公式的編號和引用正確。
特殊排版: 檢查是否有需要特殊布局(如圖文混排、分欄等)的地方,并手動調整。
6.最終定稿: 保存修改后的文檔。
關于公式處理的挑戰(zhàn)與策略
難點: DOCX 中的公式通常使用 OMML (Office Math Markup Language) 存儲,嵌套在復雜的 XML 結構中。python-docx 對此支持有限。
策略:
- 提取為圖片 (最可行): 嘗試在提取階段將公式渲染或截圖為圖片。這會丟失編輯能力,但能保證視覺效果。實現(xiàn)起來也有難度,可能需要借助其他工具或庫(如 docx2python 庫可能提供一些幫助,或者需要分析 OOXML 找到圖片表示)。
- 提取為 MathML/OMML (復雜): 解析 OOXML,提取公式的 XML 片段。但 python-docx 無法直接將這些 XML 重新插入并渲染為公式。需要非常底層的 OOXML 操作。
- 提取為近似文本 (簡單但損失精度): python-docx 讀取包含公式的段落 text 屬性時,有時會得到一個純文本的近似表示。這對于簡單公式可能夠用,但復雜公式會完全失真。
- 手動處理 (最可靠): 在腳本中識別出公式位置,插入占位符,然后在人工審閱階段手動復制/創(chuàng)建公式。
總結
這是一個多階段、結合自動化和人工的過程。
自動化強項: 重復性的內容提取、基于模板的樣式應用、初步的文本風格轉換(使用 LLM)。
人工介入點: 定義精確的提取規(guī)則、處理復雜公式、精調語言風格和術語、最終的格式微調和質量檢查。
投入時間最多的部分將是 編寫和調試提取邏輯 以及 最終的人工審閱和修正。務必從少量文檔和簡單規(guī)則開始,逐步迭代和完善你的腳本。
以上就是Python實現(xiàn)word文檔內容智能提取以及合成的詳細內容,更多關于Python word文檔內容提取與合成的資料請關注腳本之家其它相關文章!
相關文章
python scrapy框架中Request對象和Response對象的介紹
本文介紹了python基礎之scrapy框架中Request對象和Response對象的介紹,Request對象主要是用來請求數(shù)據(jù),爬取一頁的數(shù)據(jù)重新發(fā)送一個請求的時候調用,Response對象一般是由scrapy給你自動構建的,因此開發(fā)者不需要關心如何創(chuàng)建Response對象,下面來一起來了解更多內容吧2022-02-02