读《内观——葛印卡的解脱之道》时记了几百段读书笔记,一直想让这些内容能公开分享、随时随地读。于是把它做成了一个多页 HTML 电子书网页版:vip.uuunit.com。
这个项目挺有代表性——一本书变成一个可交互的阅读站,中间踩了不少坑。这篇复盘完整记录做法。
项目概况
| 项目 | 数据 |
|---|---|
| 书籍 | 《内观——葛印卡的解脱之道》(威廉·哈特) |
| 输出 | 28 页 HTML 静态站 |
| 配图 | 657 张(AI 生成 + 压缩) |
| 金句音频 | 506 条(本地 TTS) |
| 书评 | 33 条(微信读书) |
| 域名 | vip.uuunit.com |
| 托管 | Cloudflare Pages |
| 总大小 | 175MB(含音频) |
全流程
1. 内容提取 & 结构化
先解决源头问题:内容从哪来?
- EPUB:本质是 zip 包,里面是 HTML 文件。用 Python 解压 → 正则去标签 → 纯文本
- Obsidian 笔记:读书时已经按段拆分了 546 段,格式是
### {章节}-{段号} {标题}
用正则 r'### ([\\d]+)-([\\d]+) (.*?) — (.+)' 把笔记解析成结构化 JSON,每章一个文件。
ch{章节}.json → [{id, title, text: [行]}]
2. 配图生成(AI + 压缩)
每段配一张独立图,内容驱动。用 SenseNova U1-Fast 批量生成,prompt 模板:
生成出来是 2752×1536 的 PNG,每张 4MB,直接放上去网页会卡死。走压缩流水线:
PNG 4MB → PIL resize 1200px → JPEG Q80 → WebP Q75
最终单张平均 ~25KB,所有图片从 27MB 压到 492KB。所有图片加 loading="lazy",首屏只加载 hero 图。
3. 金句音频
每段提取一条金句,配语音朗读。主力是 Fish S2-Pro 梦一音色(本地 MLX 模型,需参考音频),备选 Edge TTS。
主力: Fish S2-Pro 梦一 ~15s/条 本地
备选: Edge TTS Xiaoxiao ~3s/条 在线
4. 微信读书书评
首页展示读者的真实书评,用微信读书官方 Gateway API:
POST https://i.weread.qq.com/api/agent/gateway
Authorization: Bearer ***
{"api_name": "/review/list", "bookId": "..."}
拿到 45 条书评,过滤掉短评、求资源、无意义内容后保留 33 条,按点赞数降序,首页展示 Top 3 + 「展开全部」按钮。
5. 页面设计(暗黑热带美学)
这是最有意思的部分。主题是禅修,用 暗黑热带神圣美学:
| 元素 | 值 |
|---|---|
| 底色 | #0D1412(墨玉绿) |
| 文字 | #EAE3D2(暖羊皮纸白) |
| 强调 | #C49B5B(泰式暗金) |
| 按钮 hover | #9B4B38(陶土红) |
| 字体 | Cormorant Garamond + 宋体 |
几个亮点交互:
- Hero:全屏大佛,6s 缓慢 Zoom-in + Fade-in,底部暗色渐变遮罩
- 香火粒子:CSS 模拟烟雾团 + 火星上浮,
radial-gradient+ blur - 莲花搜索框:点击花苞绽放成搜索框,6 片花瓣独立 rotate 动画
- 折叠目录:按章节分组,点击展开子页面
阅读内页是 PC 三栏布局:左目录 | 中正文 | 右名词释义。手机上自动变单栏 + 底部磨砂工具栏。
段落设计:暗金序号 ¶、首字下沉 3.2em、巴利术语金色虚线下划线(hover 弹中文)、莲花分隔线、金句块带圆形播放按钮。
阅读增强:滚动位置记忆、字号四档调节、专注模式、金句收藏、每段笔记、阅读进度条、键盘翻页、Service Worker 离线缓存。
6. 部署
选择 GitHub → Cloudflare Pages:版本管理 + 自动部署。
wrangler pages project create vipassana
wrangler pages deploy . --project-name=vipassana
# 绑定域名 vip.uuunit.com
后续更新只需 git push,Cloudflare 自动构建。
踩坑记录
这几个坑最典型,值得记下来:
坑 1:脚本互相覆盖
gen_html.py 覆盖了 gen_chapters.py 的输出,导致 ch2~ch10 页面内容为空(24KB 模板,无实际内容)。原因是 gen_html.py 的 PAGES 列表里这些页的 section_ids 是空数组。
解决:把章节目录从 gen_html.py 的 PAGES 剔除,由独立脚本管理。生成顺序固定:
gen_html.py → gen_ch1.py → gen_chapters.py
坑 2:f-string 花括号冲突
Python f-string 里嵌 JS,{ 和 } 会被当成表达式,直接 SyntaxError。
解决:JS 量大时把代码定义为独立字符串变量(三引号),再用 {变量} 插入 f-string。少量 JS 用 {{ }} 转义。
坑 3:配图太大加载慢
4MB PNG 直接放首页,手机端卡成 PPT。压缩到 1200px JPEG + WebP,加懒加载后首屏 < 200KB。
坑 4:注释默认不可见
注释默认 display:none,用户根本不知道有注释。改成默认可见,去掉 toggle。
坑 5:Cloudflare Pages 无法用 API 连 GitHub
API 报 You cannot update the source object in a Direct Uploads project。GitHub OAuth 授权必须走 Cloudflare Dashboard 手动操作。
复盘
做得对的:
- 内容先结构化(JSON)再生成页面,分工清晰
- 配图压缩流水线,4MB → 25KB,体验差距巨大
- 本地 TTS + 在线 TTS 双保险,音频不缺
- 静态站 + CDN,读取体验快且免费
可以更好的:
- 批量生成 500+ 配图耗时长,可考虑复用率控制在 2x 内
- 生成脚本之间有覆盖依赖,应该用明确的职责划分
- 首次部署的 GitHub OAuth 需要手动,流程不够全自动
一本书变成网页站,核心不是技术有多炫,而是内容结构化 + AI 增强 + 静态部署这条链路跑通了。读过的书、记过的笔记,都能这样沉淀成可分享的作品。
本文由 AI 辅助生成