尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从旧版.doc到结构化题库:老试卷的自动化解析与OCR处理流程

从旧版.doc到结构化题库:老试卷的自动化解析与OCR处理流程 简介这份资源是泰州市20052006学年度第一学期期末联考高一数学试题适合高一学生期末复习、教师命题参考或数学爱好者自测使用。试卷覆盖面广难度适中包含选择题、填空题与解答题重点考查集合、函数性质、立体几何、直线与圆、对数函数及实际应用等核心内容。资源包内共1个doc文件大小约215KB文件为完整试卷及参考答案下载后可直接浏览或打印练习。已有98人学习浏览适合希望通过典型综合题巩固基础、提升运算与逻辑推理能力的学生。通过研读试题中的体积表面积计算、三视图、函数值域、向量运算、圆与直线相交等题目可以系统检验知识掌握程度并在解答题部分体会数学建模与分类讨论思想有助于期末备考中查漏补缺。1. 为什么一份高一数学期末卷值得你专门写套脚本处理拿到“泰州市20052006学年度第一学期期末联考高一数学试题[精选].doc”这种文件最常见的情况是文件名带着年份和地区打开却发现排版稀碎、公式乱码、图片错位想筛选几道好题进自己的题库却只能在 Word 里手动复制粘贴。做过一次就知道这不是「打开另存为」能解决的问题而是一套从 doc 抽取、识别、结构化到入库的完整流程。这篇就按我实际处理这类试卷文档的路径来讲怎么把老式 .doc 里的数学内容干净地抽出来怎么对付扫描版或图片版题目怎么用大模型把一道题拆成题干、选项、答案、解析以及最后让几何图和公式不丢。整个过程用到的都是常见工具有半年以上脚本经验的人跟着走一遍就能在自己机器上复现。目标不是写个一次性小工具而是搭出一个以后任何学科试卷都能复用的处理管线。2. 先拆 .doc让文本、公式、图片各回各家2.1 老式 .doc 与新 .docx 的边界在哪里一份 2005-2006 学年的试卷文件扩展名是 .doc它可能是真正的 OLE2 二进制格式也可能只是改了个后缀的 .docx甚至可能是 RTF。这个区别决定了你能不能直接用 python-docx 处理。python-docx 只认 Office Open XML也就是 .docx遇到二进制 .doc直接抛异常。我一般先看文件头的魔数来判定file 泰州市20052006学年度第一学期期末联考高一数学试题[精选].doc输出如果是Composite Document File V2 Document说明是旧版 OLE2如果是Microsoft Word 2007说明它其实是 docx。这一步不用猜几秒钟就能确认后续该走哪条技术路线。处理 OLE2 老格式LibreOffice 的 headless 转换是当前最稳的免费方案。它不像某些在线转换工具那样会把公式渲染成低分辨率图片而是尽量保留文本和对象结构。2.2 用 LibreOffice 把 doc 批量转成 docx 和 pdfsoffice --headless --convert-to docx:Office Open XML Text --outdir ./converted 泰州市20052006学年度第一学期期末联考高一数学试题[精选].doc soffice --headless --convert-to pdf --outdir ./converted 泰州市20052006学年度第一学期期末联考高一数学试题[精选].doc第一行把旧格式转成 docx 供后续 python-docx 读取第二行同时生成一份 PDF用来核对原始排版尤其是分数、根号这类在纯文本提取后容易错位的元素。参数说明--headless表示不开图形界面--convert-to docx:Office Open XML Text里的过滤器名是固定的写成别的可能转换失败--outdir指定输出目录不写就输出到当前目录。转出来的 docx 里公式可能是 OMMLOffice Math Markup Language也可能是图片取决于源文件当初怎么录入的。这一步不做任何清洗只做格式搬家。转换后建议顺手验证文件完整性python -c from docx import Document; dDocument(./converted/泰州市20052006学年度第一学期期末联考高一数学试题[精选].docx); print(len(d.paragraphs))能打印出段落数就说明结构没坏。如果连 LibreOffice 都读不了那这份文件大概率是损坏的或加密的后续工作无从谈起。2.3 抽取正文、表格和题号层级docx 的结构是 paragraphs段落和 tables表格。数学试卷里题目通常以“1.”“2.”或“一、选择题”这类编号开头用 python-docx 遍历段落按正则匹配序号就能把题目边界切出来。import re from docx import Document doc Document(./converted/泰州市20052006学年度第一学期期末联考高一数学试题[精选].docx) lines [] for p in doc.paragraphs: text p.text.strip() if text: lines.append(text) question_start re.compile(r^[一二三四五六七八九十]、|^\d[\.、]) current_q None questions {} for line in lines: if question_start.match(line): current_q line[:20] # 截前20字做key questions[current_q] [line] elif current_q: questions[current_q].append(line)这段代码的作用是把连续段落归到最近的题号下面。正则里[一二三四五六七八九十]、匹配“一、选择题”这种大块标题\d[\.、]匹配“1.”或“1、”。匹配成功后把当前题目标题存为 key后续非题号行都追加进该 key 的列表。实际处理时要注意两点一是老试卷常有“第Ⅰ卷”这种带括号的标题我的正则没覆盖可以再加一层匹配二是选择题的 A、B、C、D 选项在源文档里可能是独立段落也可能挤在同一行这会影响后续拆题。我一般会在这一步先看questions里每个 key 的列表长度如果某个 key 下挂了几十行说明题目边界没切对需要回去调整正则。3. 遇到扫描版或图片公式OCR 要分两层做3.1 先判断哪些题目是图片很多流传的老试卷是纸质扫描后转成 doc 的打开后每道题都是一张整图。前文提到的 DOCX 抽取对这种情况完全无效。判断方法很简单统计文档里内嵌图片的数量如果图片数量和题目数量接近说明这个文档是图片型的。from docx import Document from docx.opc.constants import RELATIONSHIP_TYPE as RT doc Document(./converted/泰州市20052006学年度第一学期期末联考高一数学试题[精选].docx) img_count 0 for rel in doc.part.rels.values(): if image in rel.reltype: img_count 1 print(内嵌图片数:, img_count)内嵌图片数量明显多于正常公式数量时直接走 OCR 管道。你需要先把 docx 转成 PDF再用pdftoppm把 PDF 按页渲染成高分辨率 PNG。分辨率我习惯用 200 DPI太低识别不出上下标太高会让 OCR 变慢且收益极小。3.2 公式识别用 LaTeX-OCR正文用 PaddleOCR数学试卷 OCR 的难点不是英文和汉字而是公式。普通文字识别模型会把x^2识别成x2把分数识别成一行斜杠。处理这类文件我一般会跑两个引擎PaddleOCR 负责题目普通文本LaTeX-OCR也叫 pix2tex负责渲染出的公式图片区域。# 安装 pix2texLaTeX-OCR pip install pix2tex[gui] # 命令行识别单张公式图 latexocr --image question_formula.png实际跑的时候很少直接命令行更多是写 Python 脚本调它的 API。把所有公式图丢进去后返回的是 LaTeX 字符串比如\frac{1}{2}这种。到这里公式就算得救了。但有个坑必须提醒老试卷里的公式常常是 Word 域代码或者 MathType 嵌入对象OCR 出来的 LaTeX 只是视觉近似不是原始公式可能丢符号。遇到≤、∈、⊂这类特殊符号识别错了要人工校对。我的做法是输出两个文件原始识别结果和人工校对清单不在脚本里做强纠错因为数学符号的语义错误比字错误更隐蔽。3.3 把 OCR 结果拼回题目结构import json result { title: 泰州市20052006学年度第一学期期末联考高一数学试题[精选], questions: [ {id: 1, type: choice, text: ..., options: [A. ..., B. ...], answer: }, ] } with open(paper_parsed.json, w, encodingutf-8) as f: json.dump(result, f, ensure_asciiFalse, indent2)这段代码把 OCR 出的文本整理成 JSON核心是明确了题目结构题型类别、题干文本、选项、答案、解析。answer为空是正常的OCR 一般不识别答案后续由人工或模型补全。参数说明ensure_asciiFalse必须写否则中文字符会被转成\u转义序列既难读也不利于后续检索。indent2是为了在 Git 里 diff 时能看清楚改了什么。4. 用大模型把题目拆成结构化数据提示词设计与边界4.1 为什么要做结构化拆分OCR 得到的文本是一堆连续字符串适合人读不适合查询和组卷。你希望以后能按“函数单调性”“数列求和”筛题那就需要把每道题拆成题型、题干、选项、答案、解析、知识点标签。这个工作手工做几百道题会疯用大模型处理很合适但前提是提示词写得够清楚。4.2 提示词模板给模型的边界条件import openai client openai.OpenAI(api_keysk-xxx, base_urlhttps://your-endpoint) prompt 你是高中数学试卷解析助手。请把下面OCR得到的题目文本拆成JSON字段如下 - id: 原题号 - type: 题型选择题/填空题/解答题 - stem: 题干不含选项 - options: 选项列表选择题才有 - answer: 参考答案 - analysis: 简要解析 - tags: 知识点标签数组 要求题干保留原始条件不要化简或改写公式用LaTeX没有答案就留空字符串。 题目文本 {ocr_text} 只输出JSON不要解释。 resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], temperature0.1, ) parsed resp.choices[0].message.content提示词里三个关键设计一是定义了输出 JSON 的字段格式比让模型自由发挥稳定得多二是强调“题干不要改写”防止模型自作主张改变已知条件三是temperature0.1把随机性压到最低数学题解析不追求文采准确性优先。用大模型的另一个实际好处是能顺手生成tags知识点标签。2005 年泰州高一期末的考点大概率集中在集合、函数、数列这些章节。有了标签后面做错题本或组卷就方便了。4.3 质量校验不能省import json def validate(parsed_str): try: data json.loads(parsed_str) except json.JSONDecodeError: print(JSON解析失败模型返回了多余文本) return None if not isinstance(data, list): data [data] for q in data: assert stem in q, f题目{q.get(id)}缺stem assert q[stem].strip(), f题目{q.get(id)}的stem为空 return data这段代码做了两件事一是防模型返回 JSON 之外的多余解释常见于未加“只输出 JSON”约束时二是校验必填字段缺失。遇到JSONDecodeError时最实用的重试策略是让模型「修正上次返回结果」而不是从零再跑一次省令牌也更快。注意大模型解析结果只能当草稿答案和解析字段建议人工复核。数学解答题解法多样模型给出的是参考路径不能保证与官方标答一致。5. 画几何图不用怕从图片到 SVG 再回插文档5.1 几何题的图用什么格式存高一数学里函数图像、立体几何草图、平面向量示意图占了相当比例。这些图 OCR 提不出来但转成 docx 时大概率以 EMF 或 WMF 格式嵌入。python-docx 不支持直接读 WMF需要先转成 PNG 或 SVG。# 先解压 docx 里的 media unzip -o converted/*.docx -d docx_extracted ls docx_extracted/word/media/解压后如果media文件夹里的全是.emf文件用 Inkscape 批量转成 SVG 或 PNG。至于 2005 年的原卷里那些图是不是这样嵌入的其实不重要重要的是你手里的文件用什么格式存你就按什么流程转。5.2 用 Matplotlib 重绘函数图比抠图更高效老试卷扫描图里的函数图像往往模糊且有阴影直接放进新文档很不专业。我一般直接看题目条件用 Matplotlib 重绘。import numpy as np import matplotlib.pyplot as plt x np.linspace(-3, 3, 400) y x**2 - 2*x - 3 fig, ax plt.subplots(figsize(4, 3)) ax.plot(x, y) ax.axhline(0, colorblack, linewidth0.8) ax.axvline(0, colorblack, linewidth0.8) ax.grid(True, linestyle--, alpha0.6) plt.tight_layout() plt.savefig(function_plot.svg, formatsvg)这段代码生成二次函数图像np.linspace(-3, 3, 400)决定 x 轴范围和采样密度ax.axhline和ax.axvline画坐标轴grid打开虚线网格。输出 SVG 的好处是后续插入网页不糊转 PDF 打印也清晰。重绘时要对照原题仔细检查定义域端点开闭、交点坐标位置、渐近线有没有画。画错图比没图更误导学生这个环节最值得花时间。5.3 把 SVG 插回 Markdown 或 HTML 题库最终题库我推荐输出成 Markdown因为 GitHub、GitBook、语雀都原生支持。SVG 文件用标准图片语法引用![函数图像](./images/function_plot.svg) **第 8 题** 已知函数 $f(x) x^2 - 2x - 3$求 1. 单调区间 2. 在 $[-2, 4]$ 上的最大值与最小值。图片路径建议用相对路径方便整个题库目录整体移动。公式用$包裹的 LaTeX 写法兼容性比 Word 域代码好得多。本文还有配套的精品资源点击获取
返回列表