87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
"""Word 文档解析引擎"""
|
|
from docx import Document
|
|
from docx.paragraph import Paragraph
|
|
from docx.table import Table as DocxTable
|
|
from typing import List, Dict, Any
|
|
|
|
|
|
def parse_document(file_path: str) -> List[Dict[str, Any]]:
|
|
"""
|
|
解析 Word 文档,返回 block 列表(不含表格数据,仅标记位置)。
|
|
每个 block 包含:type, text, level, style
|
|
"""
|
|
doc = Document(file_path)
|
|
blocks = []
|
|
|
|
for para in doc.paragraphs:
|
|
block = classify_paragraph(para)
|
|
if block:
|
|
blocks.append(block)
|
|
|
|
return blocks
|
|
|
|
|
|
def classify_paragraph(para: Paragraph) -> Dict[str, Any] | None:
|
|
"""识别段落类型"""
|
|
text = para.text.strip()
|
|
style_name = para.style.name if para.style else ""
|
|
|
|
if not text and "Heading" not in style_name:
|
|
return None # 跳过空段落
|
|
|
|
if "Heading" in style_name:
|
|
level = int(style_name.replace("Heading", "").strip()) if style_name.replace("Heading", "").strip().isdigit() else 1
|
|
return {"type": "heading", "text": text, "level": level, "style": style_name}
|
|
else:
|
|
return {"type": "paragraph", "text": text, "level": 0, "style": style_name}
|
|
|
|
|
|
def parse_tables(doc: Document) -> List[Dict[str, Any]]:
|
|
"""解析 Word 表格"""
|
|
tables = []
|
|
for i, table in enumerate(doc.tables):
|
|
headers = [cell.text.strip() for cell in table.rows[0].cells] if table.rows else []
|
|
tables.append({
|
|
"type": "table",
|
|
"text": " | ".join(headers),
|
|
"level": 0,
|
|
"rows": len(table.rows),
|
|
"cols": len(table.columns),
|
|
"headers": headers,
|
|
"table_index": i,
|
|
})
|
|
return tables
|
|
|
|
|
|
def generate_block_id(index: int) -> str:
|
|
"""生成 block_id,格式:block_001"""
|
|
return f"block_{index:03d}"
|
|
|
|
|
|
def build_tree(blocks: List[Dict]) -> Dict:
|
|
"""构建层级树结构"""
|
|
tree = []
|
|
stack = [] # 存父级节点
|
|
|
|
for block in blocks:
|
|
node = {"block_id": block["block_id"], "text": block["text"][:50], "children": []}
|
|
level = block.get("level", 0)
|
|
|
|
# 回退栈
|
|
while stack and stack[-1]["level"] >= level:
|
|
stack.pop()
|
|
|
|
if stack:
|
|
parent = stack[-1]["node"]
|
|
parent["children"].append(node)
|
|
block["parent_block_id"] = parent["block_id"]
|
|
else:
|
|
tree.append(node)
|
|
block["parent_block_id"] = None
|
|
|
|
# 非叶子节点入栈
|
|
if level < 2 or block["type"] == "heading":
|
|
stack.append({"level": level, "node": node})
|
|
|
|
return {"blocks": tree}
|