Jupyter Book 生成文档站点
Jupyter Book 处理的是分析做完之后的交付环节。单篇 Notebook 适合自己反复调试,但一门课的实验指导、一份课题的完整分析记录,需要目录、章节顺序和站内搜索。Jupyter Book 把散落的 .ipynb 与 .md 组织成一个可发布、可搜索的静态站点。
R 语言用 Quarto 做同一件事,见 /r/reporting/quarto/。两者的共同点是把「写文档」和「跑代码」放进同一份源文件;区别在输入的形态,Jupyter Book 以 Notebook 为中心,Quarto 以文本为中心。
先确认装的是哪个版本
Section titled “先确认装的是哪个版本”Jupyter Book 在 2.x 换过一次内核,这一步不能跳过。1.x 基于 Sphinx,配置文件是 _config.yml 加 _toc.yml;2.x 改为基于 MyST 文档引擎(mystmd),配置文件合并成一个 myst.yml。两套命令与配置互不通用,网上多数教程仍是 1.x 的写法。
jupyter-book --version2.x 只输出一行:
v2.1.61.x 输出一组依赖版本:
Jupyter Book : 1.0.4External ToC : 1.1.0MyST-Parser : 3.0.1MyST-NB : 1.4.0Sphinx Book Theme : 1.3.0看到第二种,说明装的是 1.x。下文以 2.x 为准,1.x 的差异集中在「从 1.x 迁移」一节。
安装与初始化
Section titled “安装与初始化”pip install jupyter-book2.x 在构建时会检查 Node.js,版本不够会直接报错退出:
- node 22.23.2 Required: >= 18.0.0- npm 10.9.8 Required: >=8.6.0- myst 1.10.1在项目目录里初始化:
jupyter-book init不带参数时该命令会交互式提问,最后写出 myst.yml。要跳过提问,用显式开关:
jupyter-book init --project --site --write-toc--project 生成项目段,--site 生成站点段,--write-toc 扫描目录下的 .md 与 .ipynb 并自动填好目录。另有 --gh-pages 与 --gh-curvenote,用于顺带生成对应的部署工作流。
myst.yml:一份配置管住整个项目
Section titled “myst.yml:一份配置管住整个项目”init 生成的骨架(去掉注释):
version: 1project: id: 1c21d7a8-7610-4a58-8b75-d9dd75bb164f toc: - file: index.md - file: analysis.md
site: template: book-themeversion: 1 是配置格式的版本号,与项目版本无关。project 段管内容:title、description、authors、github、toc。site 段管外观:template 选主题,options 下可放 favicon 与 logo。
作者字段有一处容易踩的地方,中文姓名会触发警告:
project: authors: - name: 张三myst.yml 'authors.0' No given name for name '张三' - if this is intended, you may define 'name' explicitly as an object解析器按空格拆分姓名,拆不出姓与名就告警。把 name 写成包含 given 与 family 的对象即可消除:
project: authors: - name: given: 三 family: 张given 与 family 必须是 name 的子键。直接与 name 平级会被判定为多余键并忽略,警告照旧。
目录结构:toc 字段
Section titled “目录结构:toc 字段”--write-toc 按文件名字母序生成列表,正式写书时需要手工调整顺序与层级:
project: toc: - file: index.md - title: 数据准备 children: - file: cleaning.md - file: merging.md - title: 建模 children: - file: regression.md - file: nb.ipynbfile 的路径不带扩展名之外的前缀,相对于 myst.yml 所在目录。.md 与 .ipynb 可以混排在同一层,构建时一视同仁,Notebook 的单元格输出会直接渲染进页面。分组用 title 加 children,嵌套层数不设上限,侧边栏的折叠层级与这里一一对应。
index.md 必须放在最外层的第一项,它对应站点的首页;放进 children 里站点就没有入口页。
MyST 语法基础
Section titled “MyST 语法基础”Jupyter Book 2 的正文由 MyST 解析。它是 Markdown 的超集,Markdown 的写法全部有效,额外提供三类结构。
指令(directive),用三个冒号包裹,用于插入块级元素:
:::{note}样本量小于 30 时,正态性检验的结论不稳定。:::角色(role),用花括号加冒号,用于行内标记,如 {math}、{term}。
交叉引用,给标题或图表一个标签,正文里用 # 引用:
(my-section)=## 数据准备
见 [](#my-section)。标签行必须紧贴在标题上方,中间不能有空行。渲染后 [](#my-section) 自动变成章节编号,插入新章节时后续编号自动重排。
图表用 figure 指令,name 以 fig- 开头才能被引用:
```{figure} logo.png:name: fig-logo:width: 200px
站点标志。```图片路径写错时构建不中断,只在终端报一行提示,页面上的图留空:
fig.md Cannot find image "logo.png" in /tmp/jbtest本篇引用的构建日志均为日志正文,终端里每行前面还会多一个图标前缀,此处为排版省略。
执行 Notebook
Section titled “执行 Notebook”默认情况下 build 不执行 Notebook,它只读取 .ipynb 里已有的一组输出。想让构建过程重跑代码,加 --execute:
jupyter-book build --execute终端会显示每个被执行的 notebook 以及缓存命中情况:
Executing notebook (nb.ipynb) [no execution cache found]执行需要本机有可用的 Jupyter 内核。内核缺失时报错如下,构建继续,但页面用的是旧输出:
nb.ipynb Could not load Jupyter session manager to run executable nodes多个 notebook 并发执行,--execute-parallel 控制并发数,默认 3。并发数调高会同时占用更多内存,跑大模型或大数据集时按机器配置下调。
正文里要插入可执行代码块时用 {code-cell},块选项写在块内,以冒号开头:
```{code-cell} python:tags: [hide-input]
import numpy as npx = np.array([4.2, 11.5, 7.3, 5.8, 6.4, 10.0])print(f"均值 {x.mean():.2f}")```:tags: [hide-input] 让代码折叠、只留输出,写教程时常用。这与 Notebook 单元格自带的 tags 是同一套机制,两种源文件里可以写同样的标签。
jupyter-book build --site # 生成站点内容jupyter-book build --pdf # 导出 PDFjupyter-book build --word # 导出 Wordjupyter-book build --all # 全部格式--site 是网页,其余格式各自独立。构建成功的终端输出会列出每个源文件与总页数:
Built index.md in 19 ms.Built analysis.md in 18 ms.Built 2 pages for project in 47 ms.站点内容与主题是两件事。--site 先把 MyST 源文件编译成内容,再从远端拉取 site.template 指定的主题包:
Querying template metadata from https://api.mystmd.org/templates/site/myst/book-themeFetching template from https://github.com/myst-templates/book-theme/archive/refs/heads/main.zip主题包走 GitHub,网络不通时这一步失败,但前面的内容编译已经完成,输出里能看到 Built N pages。这意味着内网机器可以把主题包预先缓存到本地,或改用 --html 只产出静态页面内容。
导出的产物在 _build 目录,用 jupyter-book clean 清理。该命令只删导出物与临时文件,不动源文件。
部署到 GitHub Pages
Section titled “部署到 GitHub Pages”初始化时带上 --gh-pages,会在 .github/workflows/ 下生成一个工作流文件,推送到 main 分支后由 Actions 构建并发布。手工部署走同一套思路:构建站点,再把产物推到 gh-pages 分支。
仓库信息写进 project.github 后,构建出的页面会带上「在 GitHub 上编辑此页」的链接,读者发现笔误可以直接提 PR。这一点与 Quarto 的 repo-url 是同一个用途。
从 1.x 迁移
Section titled “从 1.x 迁移”1.x 与 2.x 的差别不是配置项改名,是整条工具链换掉了。对应关系如下:
| 1.x | 2.x |
|---|---|
_config.yml + _toc.yml |
myst.yml 单文件 |
jupyter-book create PATH |
jupyter-book init |
jupyter-book build PATH |
jupyter-book build |
format: jb-book + root: + chapters: |
project.toc 的嵌套列表 |
| 依赖 Sphinx 与 sphinx-book-theme | 依赖 Node.js 与 mystmd |
1.x 的目录文件长这样,root 指定首页,chapters 是平铺列表:
format: jb-bookroot: introchapters:- file: markdown- file: notebooks1.x 的 _config.yml 里,execute.execute_notebooks 控制是否重跑;2.x 改成命令行开关 --execute,默认不执行。迁移时最需要留意这一处,1.x 默认 force(每次都跑),2.x 默认不跑,行为相反。
已经写好的 Markdown 与 Notebook 内容不必改动,MyST 的指令与角色语法在两版之间兼容。真正要重写的是配置文件和构建命令,通常半小时之内能完成。
- 路径写错是最常见的失败原因。
toc里的file相对于myst.yml,图片路径相对于当前源文件,两套基准不一样。 - 忘记
--execute,页面显示的是上次运行的结果。Notebook 里的输出不会自动更新。 - 构建缓存。
--execute命中缓存时不会重跑,改了数据源要先jupyter-book clean。 - 中文 PDF 需要 LaTeX 环境与中文字体,
--pdf在没装 TeX 的机器上直接失败。想省事就先出网页,需要投稿时再单独处理 PDF。 - 主题包要联网拉取,离线环境必须提前缓存,否则内容编译成功而站点无法生成。
站点建好之后,把环境与随机种子固定下来才能让结果可复现,见 /python/reporting/reproducible/。如果只是想交付单篇分析给导师看,用 nbconvert 导出 HTML 就够了,见 /python/reporting/notebook-export/。