QParse
将结构化 Markdown 教案渲染为 Word(DOCX),并支持 .qmd 单文件打包分发。
典型场景:数学备课 / 习题讲义。支持:
@import分题导入$...$LaTeX 公式- 填空 / 选择 / 多选 / 简答
- 自动生成参考答案
- Markdown 表格与 HTML 表格
- 图片、分页、主题模板
.qmd打包 / 解包(资源按 SHA-256 去重)
目录结构
QParse/
├── qparse/ # 核心库
│ ├── markdwon_loader.py # Markdown 加载、@import、题号装饰
│ ├── markdwon_render.py # render.md 解析与整篇渲染
│ ├── markdwon_document/ # 文档节点树(题干/答案/表格/图片)
│ ├── docx_render/ # Pandoc + python-docx 输出 DOCX
│ ├── render_option/ # 默认主题、模板、referance.docx
│ ├── qmd_packer.py # .qmd 打包
│ ├── qmd_unpacker.py # .qmd 解包
│ └── utils/ # 空格/换行保护等工具
├── qmd_cli.py # Typer 命令行入口
├── dev.py # 开发调试脚本
├── render.md # 渲染入口配置示例
├── QParse.code-workspace # 工作区(代码 + 备课目录)
├── qmd-package-design.md # .qmd 设计说明
└── README.md
环境依赖
建议使用项目内虚拟环境:
cd d:\dev\QParse
.\venv\Scripts\activate
主要依赖:
| 包 | 用途 |
|---|---|
beautifulsoup4 |
HTML/Markdown 中间结构解析 |
pypandoc + Pandoc |
Markdown/HTML → DOCX |
python-docx |
DOCX 后处理(表格、边框、图片) |
docxcompose |
多段 DOCX 合并 |
jinja2 |
题干/答案模板渲染 |
typer |
命令行工具 |
系统需已安装 Pandoc。
快速开始
1. 为 main.md 生成 render.md
python qmd_cli.py init "d:\workspace\lesson_plan\...\main.md" -o ".\render.md" -f
生成示例:
<render src="D:\workspace\...\main.md">
<file type="docx" dist="习题版.docx">
<doc theme="Worksheet">
<after>
<pagebreak></pagebreak>
# 参考答案
<reference-answer></reference-answer>
</after>
</doc>
</file>
</render>
2. 直接渲染 DOCX
python qmd_cli.py render .\render.md
3. 打包为 .qmd 再解包渲染
# 打包
python qmd_cli.py pack .\render.md -o .\lesson.qmd
# 解包(目录保留)
python qmd_cli.py unpack .\lesson.qmd -t .\lesson_unpacked
# 渲染解包后的入口
python qmd_cli.py render .\lesson_unpacked\render.md
# 或一键:打包 → 解包 → 渲染
python qmd_cli.py build .\render.md -o .\lesson.qmd -u .\lesson_unpacked
命令行工具
入口文件:qmd_cli.py
python qmd_cli.py --help
| 命令 | 说明 |
|---|---|
init |
输入 main.md,生成默认 render.md |
pack |
将 render.md 及依赖打包为 .qmd |
unpack |
解包 .qmd 到工作目录 |
render |
根据 render.md 渲染 DOCX |
build |
打包 + 解包 + 渲染一键完成 |
init
python qmd_cli.py init <main.md> [-o render.md] [-d 习题版.docx] [--theme Worksheet] [-f]
- 默认输出到
main.md同级的render.md -o可指定输出路径(例如项目根目录)-f覆盖已存在文件
pack
python qmd_cli.py pack <render.md> [-o lesson.qmd] [-w QParse.code-workspace]
unpack
python qmd_cli.py unpack <lesson.qmd> [-t lesson_unpacked]
render
python qmd_cli.py render <render.md>
build
python qmd_cli.py build <render.md> [-o lesson.qmd] [-u lesson_unpacked]
Python API
from qparse import (
MarkdownLoader,
MarkdwonRender,
QmdPacker,
QmdUnpacker,
)
# 加载 Markdown(展开 @import,装饰题号)
loader = MarkdownLoader(r"d:\path\to\main.md")
soup = loader.get_soup()
answers = loader.get_reference_answer_markdwon()
# 按 render.md 渲染
render = MarkdwonRender(src=r"d:\dev\QParse\render.md")
outputs = render.render() # List[Path]
# 打包 / 解包
packer = QmdPacker()
qmd_path = packer.pack(entry=r"d:\dev\QParse\render.md", output=r"d:\dev\QParse\lesson.qmd")
unpacker = QmdUnpacker(str(qmd_path))
work_dir = unpacker.unpack(target=r"d:\dev\QParse\lesson_unpacked")
entry = unpacker.entry_path
开发调试可参考 dev.py:
python dev.py
Markdown 约定
分文件导入
@import "./01/main.md"
@import "./02/main.md"
题目结构(示意)
<question>
<stem>
在 $\triangle ABC$ 中,……( )。
</stem>
<span class="chiose">C</span>
</question>
题型 class:
| class | 含义 |
|---|---|
blank |
填空 |
chiose / choice |
单选 |
multiple-choice / multiple-chiose |
多选 |
answer |
简答 |
Loader 会自动:
- 为每个
<question>写入连续question_id - 标记
span[type=answer] - 写入
answer_types
参考答案
在 render.md 的 before / after 中使用:
<reference-answer></reference-answer>
生成规则:
- 填空 / 选择 / 多选:连续客观题合并为 Markdown 表格
- 简答:
题号、答案(使用顿号,避免 Pandoc 识别为有序列表) - 答案只取文本,不保留原始
<span>
示例:
| 1 | 2 |
| --- | --- |
| $\sqrt{3}-1$ | C |
3、(Ⅰ)$\frac{8\sqrt{10}}{9}$;(Ⅱ)$\frac{\sqrt{6}}{2}$
公式与换行
- 行内公式:
$...$ - 源文件中的
\n在加载阶段转为<br/>,DOCX 使用 Pandochard_line_breaks - 普通空格与 Tab 经
HtmlFullProtector保护,避免 BeautifulSoup 丢失
图片

打包时图片进入 resources/<sha256>.ext,解包后复制到文档同级 assets/。
render.md 说明
render.md 是渲染编排入口,不是正文本身。
<render src="绝对或相对路径/main.md" theme="可选主题json">
<file type="docx" dist="习题版.docx">
<doc theme="Worksheet">
<before>…</before>
<after>
<pagebreak></pagebreak>
# 参考答案
<reference-answer></reference-answer>
</after>
</doc>
</file>
</render>
要点:
src:正文main.md(相对路径相对render.md所在目录解析)file@dist:输出 DOCX 文件名/路径doc@theme:主题名(来自默认 theme 或外部 json)before/after:插入正文前后的内容<reference-answer>:自动替换为参考答案 Markdown
DOCX 渲染流程
render.md
→ MarkdownLoader 加载入口与正文
→ 展开 @import / 图片 / 题号
→ before/after 插入(含参考答案)
→ MarkdwonDocument 生成中间 Markdown/HTML buffer
→ Pandoc 转 DOCX(tex_math_dollars + hard_line_breaks + reference-doc)
→ python-docx 后处理表格样式、图片、合并
→ 复制到 dist
→ 清理本次 .qmd_runtime/<uuid>
表格相关默认配置(可在 render_option 中调整):
- 宽度 100%
- 表头加粗
- 表头底色
- 边框颜色
- 单元格居中
并发渲染时,每个 DocxRender 使用独立临时子目录,避免互相删除文件。
.qmd 打包设计(摘要)
详细设计见 qmd-package-design.md。
包内结构:
lesson.qmd
├── manifest.json
├── render.md
├── documents/ # 保持目录关系的 Markdown
├── resources/ # 二进制资源,按 SHA-256 去重
├── themes/
└── templates/
规则:
- 绝对路径资源复制后转为包内相对路径
- 相同内容只存一份(哈希去重)
- 解包时按需把资源放到各 Markdown 同级
assets/ - 编辑器可直接预览
./assets/... - 重新打包时重新计算哈希并清理未引用资源
工作区
QParse.code-workspace 通常包含:
- 代码目录:
d:\dev\QParse - 备课目录:如
02-必修2备课
打包器会读取 workspace folders,将文档放入:
documents/<index>-<folder-name>/...
避免多个工作区目录同名冲突。
常见问题
1. 简答题被识别成 Word 有序列表
参考答案使用 3、 而不是 3.。
2. 解包后找不到 main.md
MarkdwonRender 会把 render.md 中的相对 src 解析为相对 render.md 所在目录。请用解包后的 render.md 作为渲染入口。
3. 两个进程同时渲染报临时文件不存在
临时目录已改为 .qmd_runtime/<uuid>/,每个渲染实例独立。
4. 表格公式丢失
Markdown 分支使用:
markdown+tex_math_dollars+hard_line_breaks
HTML 表格分支使用:
html+tex_math_dollars
5. 图片在打包后预览失败
确认解包步骤已执行;图片应出现在对应 Markdown 同级 assets/ 下。
开发提示
- 项目内历史拼写
markdwon为既有命名,调用时请保持一致,不要只改函数名不改调用方 - 调试加载结果可写
soup.html/pv.md(见dev.py) - 修改主题与题干模板:
qparse/render_option/ - 修改 DOCX 后处理:
qparse/docx_render/write_buffer.py
许可证
内部备课工具,按团队约定使用。
Metadata
Release files for qparse 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qparse-0.1.2.tar.gz | 78.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qparse-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 160.5 kB
Release files / qparse-0.1.2.tar.gz
| Download URL | qparse-0.1.2.tar.gz |
|---|---|
| Size | 78.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca2fdc066b2d4cfe007dbb6b62a1719a91ea0a01f7186b47a05e5337bc8bb994
|
|
BLAKE2b-256 checksum How to use checksums |
6393e10acd0fe2497ee37ed478ce4e5a10676cefd11c0cfeae402af30518e205
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.18
|
Release files / qparse-0.1.2-py3-none-any.whl
| Download URL | qparse-0.1.2-py3-none-any.whl |
|---|---|
| Size | 82.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a0cbf5fc30f6cf07187946de55a0762f50a3b6ddbdf54fd6558c1fa48fa8e88c
|
|
BLAKE2b-256 checksum How to use checksums |
8628fbccd451c093cce0c052a1ba67e58e11fcb66b94b6f5a3e1e0de425c49a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.18
|