资讯动态

用python-docx解析Word练习题,实现题库结构化与批量生成随机试卷

发布时间:2026/9/20 8:55:59 来源:尧图企业网站定制
简介这份初中信息技术考试练习题资料面向初中学生及信息技术课程初学者旨在帮助备考人员系统梳理信息与计算机基础知识点。内容涵盖信息与数据区别、计算机发展历程、存储单位换算、汉字存储、软盘与文件管理、多媒体技术、Windows操作基础、计算机病毒防范等常考模块并配有大量选择题供自测巩固。题目多围绕基础概念与应用场景设计其中冯·诺依曼、ENIAC、病毒防护等易混淆点均有正误辨析适合随学随练、查漏补缺。资源为单个doc文档容量1.14MB体积小巧便于打印或随时翻阅目前已有110人在线学习使用。通过完成这套练习读者可快速定位薄弱环节熟悉考试题型与命题思路掌握文件扩展名、CPU组成、输入输出设备等高频考点为初中信息技术学业测评打下扎实基础。1. 拿到“初中信息技术考试练习题.doc”之后我在处理什么一位初中信息老师把“初中信息技术考试练习题.doc”发过来说想让两个班的学生考前各做一遍又怕题目顺序固定导致互相抄答案。直接把这个文件扔到班级群只是完成了“有题可做”这一步真正有复用价值的是文档里那几十道题本身。把一套 Word 练习题变成结构化数据再反向批量生成若干套排版好、顺序不同、选项乱序的试卷这才是处理这类 doc 文档的完整思路。这套流程落在技术上本质上是文档结构化从 Word 段落里识别题型和题目边界清洗掉题号、选项、答案的排版差异存成 Excel 题库再用脚本从题库随机抽题重排。做这个事的人不只是老师还有做题库系统、教辅工具、文档批处理脚本的开发者。下面按实际会做的顺序把“练习题.doc”从一份原始文件变成按需出题的生成流水线。2. 先读懂 doc 里练习题的文档结构用 python-docx 提取题目2.1 这类练习题文档的常见排版规律打开一份真正的“初中信息技术考试练习题.doc”按段落看排版基本是三类跟科目无关。第一类是分类标题比如“一、单项选择题”“二、判断题”“三、操作题”它们独立成段前后常有空行。第二类是题目本体选择题通常是一个题干段后面跟着四个选项段也有相当一部分把几个选项直接接在题干段后面写成一行。第三类是答案有的紧跟题目比如“答案B”出现在同一段或下一段有的放在文档末尾单独一张表里。这个规律决定了解析脚本的主线先按分类标题把文档切成若干区域再在每个区域内按题号识别题目边界。识别题号比识别题干更可靠因为题干内容无法预测而题号格式相对固定阿拉伯数字加点、数字加顿号、括号数字最多再加一个汉字序号。把格式列成一张正则对照表解析代码就完成了一半。2.2 把老 .doc 转成 .docx再交给 python-docxpython-docx 只能处理 Office Open XML 格式的 .docx目录里给的是 .doc即 OLE2 复合文档二进制格式。任何直接open()读出文本再做正则的做法都会先撞上编码墙。常见做法是用 LibreOffice 的无头模式批量转换全程不需要打开图形界面适合丢进自动化脚本里执行。soffice --headless --convert-to docx --outdir ./converted ~/downloads/初中信息技术考试练习题.doc这里--headless表示不启动 LibreOffice 窗口适合在服务器或脚本环境里运行--convert-to docx指定目标格式--outdir ./converted把转换后的文件放到指定目录最后一个参数是源文件路径。如果一次要转整个目录里的 .doc配合循环处理for f in ./raw/*.doc; do soffice --headless --convert-to docx --outdir ./converted $f done转换完成后检查 converted 目录下是否生成了同名 .docx。这一步骤常见的失败原因是 LibreOffice 安装不完整或者源文件本身被 Word 用修订模式编辑过转换结果里残留批注和修订痕迹后续清洗时需要把w:ins、w:del这类修订标记剔除。Windows 上如果没有 LibreOffice也可以用 WPS 手动另存为 docx但批量场景下推荐命令行转换方便把老师手里历年攒下的多份练习题一起处理。2.3 按段落遍历识别题目边界文档转成 docx 之后用 python-docx 按段落读取而不是把全文拼成一个字符串再做全局匹配。原因是 docx 的 paragraph 对象保留了样式、编号和图片等结构信息解析完题目之后还要带着这些信息回写新文档按段落处理能让“提取”和“生成”两端共用同一套遍历逻辑。import re from docx import Document doc Document(./converted/初中信息技术考试练习题.docx) # 分类标题和题号的正则按常见排版调整 section_pattern re.compile(r^(一|二|三|四|五|六|七|八)、(.*?)$) q_pattern re.compile(r^(\d{1,2})[\.、)]) sections [] # 存放 (分类名, 题目段落索引列表) current None for i, para in enumerate(doc.paragraphs): text para.text.strip() if not text: continue m section_pattern.match(text) if m: current {name: m.group(2), starts: []} sections.append(current) continue if current is not None and q_pattern.match(text): current[starts].append(i)这段代码先扫一遍文档记录每个分类标题的段落索引再把每个分类下第一个匹配题号格式的段落位置存下来。section_pattern匹配“一、选择题”这类标题q_pattern匹配行首的阿拉伯数字题号其中[\.、)]覆盖了“1.”“1、”“1”三种常见写法。题干里如果出现“Windows 10 的版本号是”这种句子因为数字不在行首不会被误判。识别题号只是第一步还要确定一个题目的结束位置。常见做法是把下一个题号的段落索引当作上一个题目的结束同时把每个分类标题下最后一个题目延伸到下一个分类标题之前。这样得到一组“段落索引区间”再按这些区间逐段拼接题干、选项和答案就可以进入清洗环节。注意这里使用的q_pattern只匹配两个数字以内的题号如果练习题超过 99 题需要把{1,2}改成{1,3}。3. 把提取结果清洗成标准题库先处理三个最容易脏的数据点3.1 题号、选项、答案的三种写法从段落索引切割出题目范围之后第一件事不是急着写正则而是先把文档里“题号、选项、答案”的实际写法统计一遍。同一份练习题里经常混用多种风格题号有的是“1.”有的是“1、”判断题没有选项答案有时跟在题后有时集中附在卷末。混用不是老师排版随意更多时候是历年老师在不同时间往同一个文档里追加题目造成的。常见写法可以整理成一张对照表解析时逐类处理字段常见写法对应正则片段客观题题号1. / 1、 / 1 / 1^(\d{1,3})[\.、)]选择题选项A.xx / A、xx / Axx^([A-H])[\.、]选择题选项接排A.xx B.xx C.xx D.xx([A-H])[\.、]\s*([^A-H]?)判断题答案√/× / 对 / 错[√×对错]题目后附答案答案B / 【答案】B答案[:]\s*([A-H])这张表里的正则片段要结合上下文位置使用。比如“答案B”如果出现在题目段落范围内说明答案写在题后如果题目范围内找不到“答案”说明答案在文末答案表里需要单独解析文档末尾的表格再回填。做题库清洗最忌讳的是只按一种格式写死解析逻辑一份来源不明的练习题里往往藏着两三种风格。3.2 用正则做字段拆分输出到 Excel 题库把题目范围转换成结构化数据习惯用一个函数处理单个题目先按行切分再判断每行是题干、选项还是答案最后组装成一条记录写入 DataFrame。判断题没有选项列表答案用“正确/错误”存填空题把下划线作为题干的一部分保留不单独拆字段。import pandas as pd import re def parse_question(block_lines): 把一个题目的若干行文本解析成结构化记录 text \n.join(line.strip() for line in block_lines if line.strip()) # 去掉题号例如 12. / 12、 body re.sub(r^\d{1,3}[\.、)]\s*, , text) # 找选项行行首是 A. B. C. D.或 A、B、C、D、 opt_pat re.compile(r^([A-H])[\.、]\s*(.*)$) options {} for line in block_lines: m opt_pat.match(line.strip()) if m: options[m.group(1)] m.group(2) # 找答案 ans None for line in block_lines: m re.search(r答案[:]\s*([A-H]|[√×]|[对错]), line) if m: ans m.group(1) break return {题干: body, 选项A: options.get(A, ), 选项B: options.get(B, ), 选项C: options.get(C, ), 选项D: options.get(D, ), 答案: ans or } records [] for sec in sections: starts sec[starts] for pos, start_idx in enumerate(starts): end_idx starts[pos 1] if pos 1 len(starts) else len(doc.paragraphs) lines [doc.paragraphs[i].text for i in range(start_idx, end_idx)] records.append(parse_question(lines)) df pd.DataFrame(records) df.to_excel(./题库.xlsx, indexFalse, sheet_name信息技术练习题)parse_question接收的是一个题目范围内的所有文本行。opt_pat匹配独立成行的选项如果选项是接排形式即一行里有“A.xx B.xx”当前正则不会匹配完整选项需要回退到 3.1 表格里的接排正则先按“大写字母加点”切分再写入四个选项字段。写入 Excel 时sheet_name指定工作表名indexFalse防止把行号写进表格。清洗后的题库列固定为题干、四个选项、答案判断题没有的选项列留空。注意一个容易忽略的点records里没有记录题型。判断题和选择题区分不出来后面生成试卷时无法按题型分组。建议在parse_question返回前加一个source_section参数把 2.3 节sections里的分类名写进记录生成试卷时直接按分类名分组比靠答案格式反推题型更可靠。3.3 图片题怎么处理初中信息技术考试里Word、Excel 的操作题常常配界面截图选择题里也偶尔出现“如下图所示”的引用。python-docx 不会把图片直接拼进paragraph.text图片是内联在 run 里的 drawing 元素。解析时需要访问段落内部的 XML 元素把blip标签中的 rId 映射成实际图片文件。from docx.opc.constants import RELATIONSHIP_TYPE as RT img_map {} for i, para in enumerate(doc.paragraphs): for run in para.runs: blips run._element.findall(.//{http://schemas.openxmlformats.org/drawingml/2006/main}blip) for blip in blips: rId blip.get({http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed) part doc.part.related_parts[rId] img_map[i] part.blob # 段落索引 - 图片二进制内容doc.part.related_parts是 Word 文档的关系表rId指向真实的图片部件blob是图片的字节内容。拿到字节之后按“段落索引 - 图片文件”的映射存到题库目录并在题干后标注[图片]占位符。生成新试卷时看到这个占位符就把对应图片插回文档。图片题最常踩的坑是源文档里图片被设为“浮于文字上方”此时图片不在某个段落的 run 里而是挂在更上层的 anchor 节点上按段落遍历会漏掉这种情况只能靠多存一层图片映射关系来兜底。4. 从题库批量生成多套练习题 docx用随机抽题和选项乱序解决“互相抄”4.1 先定生成规则题量、题型配比、抽题策略题库清洗完成之后直接按原顺序生成一套题没有意义学生拿到手跟原文档差异不大。批量生成的价值在于每套题抽到的题目不同、排列顺序不同、选项排列也不同。在动手写生成脚本之前先把规则写成一个参数表方便不同老师按自己的考试时长和分值调整。规则项参数说明选择题抽题数choice_count 20从题库选择题里随机抽判断题抽题数judge_count 10从题库判断题里随机抽填空题抽题数fill_count 5填空题量少时允许重复出是否打乱选项shuffle_options True每题选项重排答案同步更新生成套数paper_count 3一次生成几套独立试卷是否重新编号renumber True每套从 1 重新编号抽题策略用random.sample做无放回抽样题库量小于需求量的题型单独处理。比如填空题只有 4 道却要出 5 题就从 4 道里允许重复抽 1 道并在控制台打一行警告。这个处理逻辑必须写清楚否则后期老师发现同一套卷子里出现相同题目会质疑脚本的随机性。选项乱序只对选择题有效判断题没有选项不需要处理填空题的答案是文字描述也无法乱序。4.2 用 python-docx 生成试卷正文和答案页生成文档的结构分三块卷头、题目正文、答案页。卷头用几个居中的段落模拟正文按题型分组每组前加一个“二、判断题”风格的分组标题。答案页放到文档末尾通过add_page_break()分页方便老师把答案页单独撕下来。from docx import Document from docx.shared import Pt from docx.enum.text import WD_ALIGN_PARAGRAPH import random def build_paper(questions, paper_no, out_path): doc Document() # 卷头 title doc.add_paragraph() title.alignment WD_ALIGN_PARAGRAPH.CENTER run title.add_run(初中信息技术复习练习题第%s套 % paper_no) run.font.size Pt(16) doc.add_paragraph(班级________ 姓名________) # 客观题正文 for group_name, group_items in questions.items(): doc.add_paragraph(group_name).runs[0].bold True for idx, q in enumerate(group_items, 1): p doc.add_paragraph(%d. %s % (idx, q[题干])) for opt_key in ABCD: if q.get(选项 opt_key): doc.add_paragraph( %s. %s % (opt_key, q[选项 opt_key])) doc.add_page_break() # 每组分页方便逐题讲评 # 答案页 doc.add_page_break() doc.add_paragraph(参考答案) for group_name, group_items in questions.items(): line group_name .join(%d.%s % (i, q[答案]) for i, q in enumerate(group_items, 1)) doc.add_paragraph(line) doc.save(out_path)add_paragraph生成新段落runs[0].bold True只把分组标题加粗不影响段内其他 run。选项前用四个空格做缩进比设置首行缩进更不容易出现跨文档样式丢失的问题。add_page_break()在分组之间分页是为了让老师讲评时按组翻页不需要在阅读器里反复找位置。答案页可以单独生成到另一个文件或者生成后再用密码保护看老师的具体要求。4.3 批量生成 N 套并打乱选项顺序注意答案指针同步批量生成的核心逻辑是抽题、打乱选项、更新答案、写入文档。打乱选项这一步最容易出 bug因为random.shuffle直接作用在选项列表上时如果只交换选项文本而不交换答案的位置标记生成出来的试卷答案就是错的而且错得很隐蔽不仔细对答案根本发现不了。def shuffle_options(question): 返回选项乱序后的新题目答案同步更新 opts [(A, question[选项A]), (B, question[选项B]), (C, question[选项C]), (D, question[选项D])] opts [x for x in opts if x[1]] # 过滤空选项 random.shuffle(opts) # 打乱顺序 new_q dict(question) for new_key, (old_key, text) in zip(ABCD, opts): new_q[选项 new_key] text if old_key question[答案]: new_q[答案] new_key # 答案标记跟随选项移动 return new_qzip(ABCD, opts)把新的 A、B、C、D 位置和打乱后的选项一一对应old_key question[答案]判断移动前哪个位置是正确答案再把答案改成新位置。这一步必须放在生成段落之前因为生成时读到的已经是更新完的new_q。如果某题只有三个选项zip只会配对到 CD 选项保持为空build_paper里的if q.get(选项 opt_key)会跳过空选项不影响答案更新逻辑。批量生成多套时外层循环套一个paper_no变量把random.seed()留在脚本外控制方便复现某一次抽题结果。把随机种子写到文件名里比如练习题_第2套_seed20250912.docx。这样老师反馈“这套题太难”时可以按种子把同一套题重新生成出来看具体题目而不是面对一堆无法追溯的随机结果。种子还可以解决一个实际问题同一套题要生成 docx 和 PDF 两个版本固定种子后两个文件内容完全一致便于交叉检查。5. 生成完先校验再发出去验证脚本和三个实战坑5.1 用脚本自动校验生成文档生成完几套 docx 之后不直接发给学生先跑一遍校验脚本。目的是防止两类问题抽题时题库某些选项缺失导致排版错乱以及选项乱序后答案指针没同步。校验脚本读取生成的 docx把每道题重新解析一遍检查答案是否落在 ABCD 之一、每个题目下的选项数是否为 4、题干是否为空。python check_papers.py ./output校验脚本内部逻辑很简单对每个 docx 遍历段落遇到形如“1. 题干”的段落后统计接下来四个段落的缩进和首字符对照源题库记录检查答案字段是否变化。如果发现答案不在选项范围内直接输出文件名和题号。这个脚本不追求解析所有题目样式覆盖“能抓到的那部分”就足以发现九成问题剩下的人工抽查一遍打印稿。5.2 三个实战坑旧 doc 兼容性、编号错乱、图片失效第一个坑是把 .doc 直接改扩展名成 .docx。文件内容还是 OLE2 二进制Windows 文件资源管理器可能根据扩展名尝试打开但任何严格解析 OLE2 的库都会报错python-docx 会直接抛PackageNotFoundError。判断文件是否真的转换成功可以用file命令看文件头file ./converted/初中信息技术考试练习题.docx输出包含Zip archive或Microsoft Word 2007字样说明是真 docx如果输出Composite Document File说明转换没生效文件还是老格式需要回到第 2 章重新检查 LibreOffice 的转换命令。第二个坑是正则匹配题号把题干里的数字误判。典型例子是“2.下列关于……”如果题干以数字开头会被当成新题号切分导致上一题内容被拦腰截断。应对办法是题号正则只用于行首并让解析器记录已经出现过的题号序列如果出现不连续跳变优先怀疑是误匹配退回上一步检查该行的完整内容。第三个坑是图片失效。题库里存的图片二进制如果在生成时写错路径试卷里会留下一个空占位符。做法是在生成脚本里定义一个统一的IMG_DIR基准目录所有图片用相对IMG_DIR的路径写入不使用D:\xxx这种绝对路径避免换电脑后整批文档找不到图。生成答案页时只输出题号和答案字母不输出图片引用可以避免答案页里出现大段截图、打印浪费纸张。5.3 把 docx 批量转 PDF 再分发顺便控制文件体积最后一步常用命令是把生成的 docx 批量转成 PDF 再发到班级群。PDF 在各端排版稳定学生手机打开不会因为缺少字体出现错位或空白。还是用 LibreOffice 无头模式for f in ./output/*.docx; do soffice --headless --convert-to pdf --outdir ./pdf_out $f done--convert-to pdf是 LibreOffice 内置的 PDF 导出过滤参数--outdir ./pdf_out指定输出目录循环里的$f带引号是防止文件名含空格或中文。转换完成后检查 pdf_out 目录里的文件数量是否和 docx 数量一致再用ls -lh看一眼文件大小。如果 PDF 超过 5MB多半是里面的截图没有压缩可以在题库阶段统一把图片缩放到 800px 宽再存体积能降一半以上且打印时清晰度完全够用。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价