Files
crawl4zeroerr/zeroerr_crawler/post_process.py

165 lines
7.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Word 文档后处理模块
优化生成的 Word 文档格式
"""
import re
from docx import Document
def post_process_docx_headings(docx_path: str):
"""
后处理 Word 文档:优化相同层级的连续标题
规则:
1. 如果两个相同层级的连续标题之间没有文字内容和图片
2. 如果一个标题包含另一个,则保留较长的
3. 如果不包含,则合并为一个标题
4. 如果中间有图片,不合并
Args:
docx_path: Word 文档路径
"""
try:
doc = Document(docx_path)
paragraphs = doc.paragraphs
# 找到所有标题段落及其索引
heading_indices = []
for i, para in enumerate(paragraphs):
if para.style.name.startswith('Heading'):
# 提取标题级别Heading 1 -> 1, Heading 2 -> 2, ...
level_match = re.search(r'Heading\s+(\d+)', para.style.name)
if level_match:
level = int(level_match.group(1))
text = para.text.strip()
if text: # 只处理非空标题
heading_indices.append({
'index': i,
'level': level,
'text': text,
'paragraph': para
})
if len(heading_indices) < 2:
return # 至少需要两个标题才能合并
# 需要删除的段落索引
to_remove = set()
# 需要修改的段落(合并标题)
to_modify = {}
i = 0
while i < len(heading_indices) - 1:
current = heading_indices[i]
next_heading = heading_indices[i + 1]
# 只处理相同层级的连续标题
if current['level'] == next_heading['level']:
# 检查两个标题之间是否有文字内容或图片
start_idx = current['index'] + 1
end_idx = next_heading['index']
has_content = False
for j in range(start_idx, end_idx):
para = paragraphs[j]
# 如果遇到其他标题,说明不是连续的
if para.style.name.startswith('Heading'):
has_content = True
break
# 检查是否有图片(通过检查段落中的 drawing 元素)
has_image = False
try:
# 方法1: 检查段落 XML 中是否包含 drawing 标签
if hasattr(para, '_element'):
para_xml = para._element.xml if hasattr(para._element, 'xml') else str(para._element)
if 'drawing' in para_xml.lower():
has_image = True
# 方法2: 检查段落中的运行runs是否有图片
if not has_image and hasattr(para, 'runs'):
for run in para.runs:
if hasattr(run, '_element'):
try:
run_xml = run._element.xml if hasattr(run._element, 'xml') else str(run._element)
if 'drawing' in run_xml.lower():
has_image = True
break
except:
pass
except Exception:
# 如果检查失败,保守处理:假设有内容,不合并
pass
if has_image:
has_content = True
break
# 检查是否有文字内容(非标题段落)
text = para.text.strip()
if text:
has_content = True
break
# 如果中间没有文字内容,需要处理
if not has_content:
current_text = current['text']
next_text = next_heading['text']
# 判断包含关系(较短的标题是否包含在较长的标题中)
if len(current_text) <= len(next_text):
# 当前标题较短,检查是否包含在下一个标题中
if current_text in next_text:
# 当前标题包含在下一个标题中,保留较长的(下一个)
to_remove.add(current['index'])
print(f" 标题优化: 删除 '{current_text}'(包含在 '{next_text}' 中)")
i += 1 # 跳过下一个标题,继续检查
continue
else:
# 下一个标题较短,检查是否包含在当前标题中
if next_text in current_text:
# 下一个标题包含在当前标题中,保留较长的(当前)
to_remove.add(next_heading['index'])
print(f" 标题优化: 删除 '{next_text}'(包含在 '{current_text}' 中)")
i += 1 # 继续检查当前标题与下一个标题
continue
# 不包含,合并标题
merged_text = f"{current_text} {next_text}"
to_modify[current['index']] = merged_text
to_remove.add(next_heading['index'])
print(f" 标题优化: 合并 '{current_text}''{next_text}''{merged_text}'")
# 更新当前标题文本,以便继续检查与下一个标题的关系
current['text'] = merged_text
i += 1 # 跳过下一个标题,但继续用合并后的标题检查
continue
i += 1
# 应用修改
if to_remove or to_modify:
# 修改合并的标题
for idx, merged_text in to_modify.items():
para = paragraphs[idx]
para.clear()
para.add_run(merged_text)
# 删除需要移除的标题(清空内容并改为普通段落)
for idx in sorted(to_remove, reverse=True):
para = paragraphs[idx]
# 清空段落内容
para.clear()
# 改为普通段落样式(避免保留标题样式)
para.style = doc.styles['Normal']
# 保存文档
doc.save(docx_path)
total_changes = len(to_remove) + len(to_modify)
print(f" 标题优化完成: 处理了 {total_changes} 个标题(删除 {len(to_remove)} 个,合并 {len(to_modify)} 个)")
except Exception as e:
print(f" 警告: 标题后处理失败: {e}")
# 失败不影响原始文档,继续执行