用Python批量整理Word课题申报书:格式转换到结构化归档 📅 发布时间:2026/9/19 13:15:35 👁 浏览次数: 简介压缩包内为一份2021-2022年收藏的江苏省研究生教育教学改革研究与实践课题资料doc格式整体仅255KB。文档对应江苏省2015年度研究生教育教学改革研究与实践课题省立省助的113项课题清单逐项列出课题序号、所在学校、课题名称、主持人与备注信息。内容覆盖南京大学、苏州大学、东南大学、南京航空航天大学、南京理工大学、江苏科技大学、中国矿业大学、南京工业大学等高校涉及课程改革、招生方式改革、国际评估、实践基地建设、论文质量评价、研究生国际化培养等方向适合高校研究生教学管理人员、课题申报者及教研人员参考。资源共1个doc文件结构清晰便于检索与打印已有125人学习浏览。读者可借此快速把握江苏省研究生教育改革的整体布局与具体课题选题了解不同类型院校的教研切入点为撰写申报书、设计研究方案提供直接参照。1. 这批江苏研究生教改课题文档值得用工程方法重新整理手头有一批 2021-2022 年收藏的「江苏研究生教育教学改革研究与实践课题」文档格式是 .doc文件名和内容都带着申报书、结题报告、立项名单等典型教育口材料特征。这类资料真正让人头疼的不是读而是找——几十上百个文件堆在一个目录里文件名参差不齐想按学校、按年度、按研究方向筛一遍靠人工翻 Word 的办法基本不可行。这篇文章要解决的就是这个场景把一批 .doc 格式的教改课题文档用 Python 批量读取、清洗、结构化再按课题名称、负责人、单位、方向等维度归档成 Excel 或 SQLite让后续检索从「打开文件一个个翻」变成「一条 SQL 查出来」。适合需要处理批量 Word 文档的运维、数据分析、教育信息化相关从业者也适合帮导师整理申报材料的同学照着一遍跑通。核心思路并不复杂doc 格式没有统一文本接口先统一转成 docx 或直接抽文本再按申报书结构做规则解析最后落到结构化表格和文件重命名。整套流程在一台普通笔记本上就能跑完不需要额外服务。2. 批量读取 .doc 文件先解决格式兼容再谈解析2.1 为什么 python-docx 打不开 .doc 文件python-docx 只支持 .docx这是大多数人在第一步就卡住的地方。.doc 是 OLE 复合文档格式.docx 是 OOXML 格式两者底层结构完全不同。直接改扩展名没有用二进制内容不会因为后缀变了就变成另一种格式。常见做法有三种一是用 LibreOffice 无头模式批量转换二是用 Windows 上的 win32com 调 Word 程序转换三是用 textract 或 antiword 直接抽文本。从可控性和后续扩展来看建议优先走 win32com 或 LibreOffice 方案因为转换后得到的是标准 .docx后续还能用 python-docx 做精细结构解析。2.2 用 win32com 把 doc 批量转成 docximport os import win32com.client def convert_doc_to_docx(src_dir, dst_dir): word win32com.client.Dispatch(Word.Application) word.Visible False word.DisplayAlerts 0 os.makedirs(dst_dir, exist_okTrue) for fname in os.listdir(src_dir): if not fname.lower().endswith(.doc): continue doc_path os.path.join(src_dir, fname) new_name os.path.splitext(fname)[0] .docx docx_path os.path.join(dst_dir, new_name) doc word.Documents.Open(doc_path) doc.SaveAs2(docx_path, FileFormat16) # 16 代表 docx doc.Close() word.Quit() convert_doc_to_docx(./raw_docs, ./converted_docs)这段代码在 Windows 且已安装 Microsoft Word 的环境下运行。FileFormat16是 wdFormatXMLDocument 的常量值保存为 .docxDisplayAlerts0用来屏蔽弹窗避免转换过程中人工确认。转换完成后如果发现某些文件没转换成功多半是原文档处于只读保护或被占用检查doc.Close()是否在异常分支里被跳过。2.3 没装 Office 的服务器环境用 LibreOfficeLinux 服务器或没有授权 Word 的 CI 机器上用 LibreOffice 的 soffice 命令更实际soffice --headless --convert-to docx --outdir ./converted_docs ./raw_docs/*.doc--headless表示不启动图形界面--convert-to docx把输入文件转为 docx--outdir指定输出目录。批量转换时通配符可以一次处理整个目录。转换结果的文件名保持原名不变只替换后缀。需要注意一个坑强制转换后 docx 里可能会残留分页符、页眉页脚等对文本抽取干扰不大的内容但如果原 doc 里有文本框或域代码转换后可能变成图片或丢失。拿到转换结果后建议先随机打开 3 到 5 个文件抽查别直接批量处理到底。3. 从 docx 中抽取正文段落和表格3.1 用 python-docx 读取段落顺序转成 docx 之后用 python-docx 可以按文档流顺序读取段落。江苏省研究生教改课题申报书通常包含课题名称、申报单位、项目负责人、课题组成员、研究内容、研究目标、创新点、经费预算等段落这些字段不是统一模板需要从段落文本里按特征词识别。from docx import Document def extract_paragraphs(docx_path): doc Document(docx_path) for para in doc.paragraphs: text para.text.strip() if text: print(text) extract_paragraphs(./converted_docs/某课题申报书.docx)这里没做任何过滤先把所有非空段落按顺序打印出来目的是摸清文档实际结构。不同年份、不同学校的课题申报书模板略有差异先跑一遍看段落分布再决定后续规则怎么写。3.2 把表格数据一并读出来申报书里除了正文段落经费预算、成员分工、研究计划这几部分常用表格承载。表格内容的抽取方式与段落不同需要对每个 table 对象逐行逐列读取from docx import Document def extract_tables(docx_path): doc Document(docx_path) for idx, table in enumerate(doc.tables): print(f--- Table {idx 1} ---) for row in table.rows: cells [cell.text.strip() for cell in row.cells] print( | .join(cells)) extract_tables(./converted_docs/某课题申报书.docx)doc.tables返回文档中的所有表格对象row.cells拿到的单元格顺序可能与视觉顺序不一致因为 python-docx 对合并单元格会重复返回同一个 cell 对象。遇到带合并单元格的表格打印出来看一下再决定是否按行索引取值。3.3 抽取常见字段的规则模板先定义一批特征关键词比如「课题名称」「申报单位」「项目负责人」「联系电话」「研究方向」然后在段落或表格单元格里做包含匹配。这个方案不算优雅但对这类格式不固定的申报书来说最稳。import re FIELD_RULES [ (r课题名称[:]\s*(.), topic), (r申报单位[:]\s*(.), institution), (r项目负责人[:]\s*(.), leader), (r研究起止时间[:]\s*(.), duration), ] def parse_fields(text): result {} for pattern, field_name in FIELD_RULES: m re.search(pattern, text) if m: result[field_name] m.group(1).strip() return result实际文档里的冒号有全角半角混用的情况模式里用[:]同时兼容。如果某字段在段落里找不到再遍历表格中的单元格做同样的匹配。抽取结果要以人工抽检 10 个文档的准确率作为验收标准不用追求百分百后续还有清洗兜底。4. 清洗文本与信息结构化把申报内容变成行数据4.1 编码与噪声处理从 Word 里抽出来的文本往往带着全角空格、不间断空格、页码、页眉页脚残留。这些噪声不影响人眼阅读进入 DataFrame 后就变成检索和去重的麻烦。import re def clean_text(text): if not text: return text text.replace(\u3000, ).replace(\xa0, ) text re.sub(r[ \t], , text) text re.sub(r\n{3,}, \n\n, text) return text.strip()\u3000是全角空格\xa0是不间断空格这两类字符在 Word 文本中很常见但肉眼难辨。连续多个换行压缩为两个避免解析段落时出现大量空行。清洗规则不要一次写太多每加一条规则就重新跑一遍样例防止把有意义的缩进或换行也清掉。4.2 用 pandas 把多个文档汇总成一张表每个文档解析完成后把字段结果放入一个字典随后统一交给 pandas 构造成 DataFrameimport os import pandas as pd from docx import Document def process_all_docs(docx_dir): rows [] for fname in os.listdir(docx_dir): if not fname.endswith(.docx): continue doc Document(os.path.join(docx_dir, fname)) raw_text \n.join([p.text for p in doc.paragraphs]) fields parse_fields(raw_text) fields[file_name] fname rows.append(fields) return pd.DataFrame(rows) df process_all_docs(./converted_docs) df.to_excel(./jiangsu_education_topics.xlsx, indexFalse)这样做完每个申报书变成一行记录列是解析出的字段。找不到的字段留空不拦截整个流程。Excel 输出的好处是可以直接交给不写代码的人继续人工补录如果后续要频繁查询建议同步导出一份 CSV 或 SQLite。4.3 数据质量抽检建议生成 Excel 后重点检查三列课题名称是否带上了「校级」「一般项目」等后缀负责人字段是否有「教授」「博士」等职称混入申报单位是否统一成学校全称。这些问题的根源在原始模板的不规范解析层只能兜住一部分。可以用 pandas 快速看每列的缺失率和重复情况print(df.isna().mean().round(2)) print(df[institution].value_counts().head(10))isna()按列统计缺失比例定位哪些字段普遍没抽到value_counts()看学校名称分布暴露名不统一的情况。发现异常后回到对应的 docx 确认是规则遗漏还是文档本身丢字段再补规则或人工修正。5. 批量重命名归档与重复项排查5.1 按「学校-年份-负责人」重命名文件原始文件名往往是「申报书(1).doc」「新建文档.docx」这类无意义命名。既然已经解析出结构化字段直接按字段重命名文件归档更直观。import os import re def safe_filename(text): text re.sub(r[\\/:*?\|], _, text) return text.strip() def rename_by_fields(folder, df): for _, row in df.iterrows(): old_path os.path.join(folder, row[file_name]) parts [row.get(institution, ), row.get(year, ), row.get(leader, )] parts [safe_filename(p) for p in parts if p] new_name _.join(parts) .docx new_path os.path.join(folder, new_name) if old_path ! new_path and os.path.exists(old_path): os.rename(old_path, new_path)safe_filename把 Windows 文件名中不允许的字符替换成下划线防止生成非法路径。重命名前建议先打印出old_path - new_path映射清单确认无误再执行。批量重命名一旦出错文件名就回不去了所以先 dry-run 是必须步骤。5.2 用文件哈希去重收藏的课题资料里经常出现同一份申报书被保存多次的情况文件名不同但内容完全一样。按文件哈希去重是最可靠的手段对 docx 和原始 doc 都适用。import hashlib import os def file_md5(path, chunk_size8192): h hashlib.md5() with open(path, rb) as f: while chunk : f.read(chunk_size): h.update(chunk) return h.hexdigest() def find_duplicates(folder): seen {} duplicates [] for fname in os.listdir(folder): fpath os.path.join(folder, fname) if not os.path.isfile(fpath): continue digest file_md5(fpath) if digest in seen: duplicates.append((fname, seen[digest])) else: seen[digest] fname return duplicateschunk_size8192表示每次读 8KB 计算哈希避免大文件一次性载入内存。返回的列表里每一项是「重复文件-原始文件」配对。去重前确认两份文件确实不需要保留不同版本比如一份是申报书草稿、一份是最终版内容完全一致才算重复。5.3 目录结构建议归档后的目录按「年份/学校」二级结构组织即可不需要更深。学校用规范全名年份取申报书里写的研究起止时间或立项时间。如果字段解析失败就把文件放到「待整理」目录等人工处理。文件夹里放一个 README.txt记录转换日期、命名规则、解析脚本名方便日后回溯。6. 检索与复用用 Python 按关键词和时序快速检索课题库6.1 关键词命中定位课题名称、研究内容这些文本字段入库后按关键词模糊查询就能把相关课题捞出来。直接操作 DataFrame 用str.contains即可。def search_topics(df, keyword): mask ( df[topic].str.contains(keyword, naFalse) | df[research_content].str.contains(keyword, naFalse) | df[innovation].str.contains(keyword, naFalse) ) return df[mask] result search_topics(df, 产教融合) print(result[[topic, institution, leader]].head())naFalse确保缺失值不参与匹配避免报错。同时查多个字段比只查课题名称召回率高适合在不知道课题具体叫法、只记得研究倾向时使用。如果命中结果太多就加年度或学校过滤条件再收窄。6.2 时序比较2021 与 2022 年立项方向差异这批资料覆盖 2021 到 2022 两个年度可以做简单的横向对比看哪些研究主题在这两年里出现了明显增长或消退。按年度分组后统计关键词出现次数year_stats df.groupby(year)[topic].apply( lambda s: s.str.contains(课程思政, naFalse).sum() ) print(year_stats)groupby 后 apply 对每个年度子集的 topic 列做关键词计数。这个结果只反映关键词在课题名称中的命中情况不代表全部研究方向但用于发现趋势线索已经够用。更深入的分析可以把所有研究内容文本拼接后做高频词统计。6.3 给教学管理人员用的 SQLite 查询方式如果最终使用方是需要写 SQL 的管理人员或数据分析岗把数据落进 SQLite 更合适。用 pandas 的to_sql一条命令就能建表import sqlite3 conn sqlite3.connect(./topics.db) df.to_sql(topics, conn, if_existsreplace, indexFalse) conn.close()之后在任意 SQLite 工具里执行SELECT institution, leader, topic FROM topics WHERE topic LIKE %实践教学% AND year 2022 ORDER BY institution;LIKE %实践教学%与 pandas 的str.contains等价。SQLite 单文件特性方便把数据库连同查询脚本发给同事不需要安装数据库服务。如果后续数据量超过几十万条再考虑迁到 PostgreSQL 也不迟当前这个量级 SQLite 完全够用。6.4 检索结果输出为 Markdown 报告查询结果除了给 Excel还可以直接拼接成 Markdown 表格便于贴进周报或同步到内部文档系统。写一个简单的导出函数把 DataFrame 渲染成表格文本粘到任何 Markdown 编辑器里即可展示。def df_to_markdown(df, columnsNone): if columns: df df[columns] return df.to_markdown(indexFalse) markdown_text df_to_markdown(result, [topic, institution, leader]) print(markdown_text)这里用 pandas 内置的to_markdown方法需要安装tabulate库一行命令即可补齐依赖。输出的表格直接可读包含列名和数据行不需要额外排版。往内网 Wiki 或知识库粘贴时这个格式比 Excel 截图更受同事欢迎。这批课题文档整理进去之后后续再拿到 2023 年、2024 年的资料只需要跑一遍转换脚本再执行同一套解析流程数据表就自动追加新记录。做一次成体系的整理后面每次归档成本都低得多。本文还有配套的精品资源点击获取