-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoc_parser.py
More file actions
333 lines (272 loc) · 11.3 KB
/
Copy pathdoc_parser.py
File metadata and controls
333 lines (272 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
"""
文档解析模块 - 深度学习报告模板格式
支持 .docx 和 .doc 格式,提取标题层级、字数统计、实验数量等
"""
import os
import re
import glob
import struct
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
import olefile
class TemplateLearner:
"""深度报告模板学习器"""
def __init__(self, reports_dir):
self.reports_dir = reports_dir
self.templates = []
self.learned_patterns = {
'title_patterns': [],
'heading_patterns': [],
'section_structures': [],
'avg_word_count': 0,
'avg_exp_count': 0,
'font_styles': {},
'header_fields': []
}
self.learned = False
def learn_all(self):
"""学习所有报告文件"""
if not os.path.exists(self.reports_dir):
return False
files = glob.glob(os.path.join(self.reports_dir, "*.doc*"))
if not files:
return False
for filepath in files:
try:
template = self._parse_file(filepath)
if template:
self.templates.append(template)
except Exception as e:
print(f"解析失败 {filepath}: {e}")
if self.templates:
self._deep_analyze()
self.learned = True
print(f"成功学习 {len(self.templates)} 个模板,平均字数: {self.learned_patterns['avg_word_count']}")
return True
return False
def _parse_file(self, filepath):
"""解析单个文件"""
if filepath.endswith('.docx'):
return self._parse_docx(filepath)
elif filepath.endswith('.doc'):
return self._parse_doc(filepath)
return None
def _parse_docx(self, filepath):
"""解析 .docx"""
doc = Document(filepath)
template = {
'filename': os.path.basename(filepath),
'type': 'docx',
'title': '',
'total_words': 0,
'experiments': [],
'headings': [],
'header_info': {},
'paragraphs': []
}
full_text = []
for para in doc.paragraphs:
text = para.text.strip()
if text:
full_text.append(text)
template['total_words'] += len(text)
# 检测标题层级
heading_info = self._detect_heading_level(text, para)
if heading_info:
template['headings'].append(heading_info)
# 检测实验
if self._is_experiment_title(text):
template['experiments'].append(text)
# 提取字体信息
if para.runs:
run = para.runs[0]
template['paragraphs'].append({
'text': text,
'style': para.style.name if para.style else 'Normal',
'font_size': run.font.size.pt if run.font.size else None,
'font_name': run.font.name,
'bold': run.font.bold,
'alignment': str(para.alignment)
})
template['full_text'] = '\n'.join(full_text)
return template
def _parse_doc(self, filepath):
"""解析 .doc (旧格式)"""
try:
ole = olefile.OleFileIO(filepath)
template = {
'filename': os.path.basename(filepath),
'type': 'doc',
'title': '',
'total_words': 0,
'experiments': [],
'headings': [],
'header_info': {},
'paragraphs': []
}
if ole.exists('WordDocument'):
word_stream = ole.openstream('WordDocument').read()
# 从FIB提取文本
fc_min = struct.unpack('<I', word_stream[0x18:0x1C])[0]
ccp_text = struct.unpack('<I', word_stream[0x4C:0x50])[0]
text_data = word_stream[fc_min:fc_min + ccp_text * 2]
text = text_data.decode('utf-16le', errors='ignore')
# 按段落分割
paragraphs = text.split('\r')
full_text = []
for p in paragraphs:
p = p.strip()
if p:
full_text.append(p)
template['total_words'] += len(p)
# 检测标题
heading_info = self._detect_heading_level(p, None)
if heading_info:
template['headings'].append(heading_info)
# 检测实验
if self._is_experiment_title(p):
template['experiments'].append(p)
template['paragraphs'].append({
'text': p,
'style': 'Normal',
'font_size': None,
'font_name': None,
'bold': False
})
template['full_text'] = '\n'.join(full_text)
ole.close()
return template
except Exception as e:
print(f"解析.doc失败: {e}")
return None
def _detect_heading_level(self, text, para):
"""检测标题层级"""
text = text.strip()
# 大标题: 实验一、实验二等
if re.match(r'^实验[一二三四五六七八九十]+[、\s]', text) and len(text) < 80:
return {'level': 0, 'text': text, 'type': 'experiment_title'}
# 一级标题: 一、二、三...
if re.match(r'^[一二三四五六七八九十]+、', text) and len(text) < 50:
return {'level': 1, 'text': text, 'type': 'heading_1'}
# 二级标题: 1.1, 2.1, 3.1...
if re.match(r'^\d+\.\d+', text) and len(text) < 60:
return {'level': 2, 'text': text, 'type': 'heading_2'}
# 三级标题: 1., 2., 3. (后面不是数字)
if re.match(r'^\d+\.\s', text) and len(text) < 80:
return {'level': 3, 'text': text, 'type': 'heading_3'}
return None
def _is_experiment_title(self, text):
"""检测是否是实验标题"""
patterns = [
r'^实验[一二三四五六七八九十]+[、\s]',
r'实验名称[::]',
]
for p in patterns:
if re.search(p, text):
return True
return False
def _deep_analyze(self):
"""深度分析模板结构"""
if not self.templates:
return
# 统计字数
total_words = sum(t['total_words'] for t in self.templates)
self.learned_patterns['avg_word_count'] = total_words // len(self.templates)
# 统计实验数量
total_exps = sum(len(t['experiments']) for t in self.templates)
self.learned_patterns['avg_exp_count'] = total_exps // len(self.templates)
# 收集标题模式
all_headings = []
for t in self.templates:
all_headings.extend(t['headings'])
# 分析一级标题结构
h1_patterns = {}
for h in all_headings:
if h['level'] == 1:
text = h['text']
# 提取关键词
keywords = re.findall(r'[\u4e00-\u9fa5]+', text)
for kw in keywords:
h1_patterns[kw] = h1_patterns.get(kw, 0) + 1
self.learned_patterns['heading_patterns'] = sorted(
h1_patterns.items(), key=lambda x: x[1], reverse=True
)[:20]
# 分析字体大小
font_sizes = []
for t in self.templates:
for p in t['paragraphs']:
if p.get('font_size'):
font_sizes.append(p['font_size'])
if font_sizes:
from collections import Counter
size_counter = Counter(font_sizes)
most_common = size_counter.most_common(1)
if most_common:
self.learned_patterns['common_font_size'] = most_common[0][0]
self.learned_patterns['title_font_size'] = max(font_sizes)
# 收集表头字段
header_fields = set()
for t in self.templates:
text = t.get('full_text', '')
fields = re.findall(r'(课程名称|实验名称|实验地点|专业班级|学号|学生姓名|指导教师|日期)[::]', text)
header_fields.update(fields)
self.learned_patterns['header_fields'] = list(header_fields)
def get_template_info(self):
"""获取学习到的模板信息"""
if not self.learned:
return None
return {
'patterns': self.learned_patterns,
'template_count': len(self.templates),
'sample_template': self.templates[0] if self.templates else None
}
def get_avg_word_count(self):
"""获取平均字数"""
return self.learned_patterns.get('avg_word_count', 5000)
def get_avg_exp_count(self):
"""获取平均实验数量"""
return self.learned_patterns.get('avg_exp_count', 1)
def get_heading_patterns(self):
"""获取标题模式"""
return self.learned_patterns.get('heading_patterns', [])
class DocParser:
"""文档解析器"""
def __init__(self):
self.learner = None
def learn_templates(self, reports_dir):
"""学习模板"""
self.learner = TemplateLearner(reports_dir)
return self.learner.learn_all()
def get_template_info(self):
"""获取模板信息"""
if self.learner:
return self.learner.get_template_info()
return None
def get_avg_word_count(self):
"""获取平均字数要求"""
if self.learner:
return self.learner.get_avg_word_count()
return 5000
def get_avg_exp_count(self):
"""获取平均实验数量"""
if self.learner:
return self.learner.get_avg_exp_count()
return 1
def parse(self, file_path):
"""解析单个文档"""
if not os.path.exists(file_path):
return None
learner = TemplateLearner(os.path.dirname(file_path))
return learner._parse_file(file_path)
if __name__ == "__main__":
parser = DocParser()
reports_dir = r"D:\HuaweiMoveData\Users\张文瑞\Desktop\作业提交\已完成报告"
if parser.learn_templates(reports_dir):
info = parser.get_template_info()
print(f"模板数量: {info['template_count']}")
print(f"平均字数: {info['patterns']['avg_word_count']}")
print(f"平均实验数: {info['patterns']['avg_exp_count']}")
print(f"标题模式: {info['patterns']['heading_patterns'][:10]}")
else:
print("学习失败")