读《内观——葛印卡的解脱之道》时记了几百段读书笔记,一直想让这些内容能公开分享、随时随地读。于是把它做成了一个多页 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 模板:

f l a t i l l u s t r a t i o n , { } , { } , { } , n o t e x t n o l e t t e r s

生成出来是 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 辅助生成