攻克微信读书 Web 端「漏抓 40% 内容」顽疾:我开源了一个全书无损导出精排 PDF / EPUB / Markdown 工具

0 阅读5分钟

开源仓库地址:github.com/WANGXIN-567… (如果这个工具对你的深度学习与知识库整理有帮助,欢迎点亮一颗 Star ⭐ 支持!)


一、背景与痛点:为什么我决定自己造轮子?

平时在精读专业书籍、历史典籍或中医经典(比如《伤寒论》解读)时,我有三个强烈的个人学习需求:

  1. 全文检索与双链笔记:希望将整本书无损导出为干干净净的 Markdown,放进 Obsidian / Notion 建立个人知识库;
  2. iPad 大字号手写批注:希望导出一份排版考究、带侧边栏书签大纲(Outline)的 大字号精排 PDF(正文用温润的苹方,核心引文/古籍条文用华文楷体);
  3. 跨设备阅读:同时生成一份带完整目录树与原书高清插图的 标准 EPUB。

然而,在尝试了市面上现有的几款微信读书 Web 端导出脚本后,我发现它们在实际使用中普遍存在两个致命问题:

  • 痛点 1:悄无声息地漏掉全书 35%~45% 的内容 比如一本 21 万字、包含 398 条原文的书,用传统脚本跑完居然只有 12 万字、217 条原文,每一章的下半部分都莫名其妙“蒸发”了。
  • 痛点 2:跨章或短页文字交错乱码、原书排版层级全部丢失 导出来的文本要么两页文字穿插在一起,要么标题、引文、正文糊成一团,还经常因为页面折行把一句话切成两段。

为了彻底解决这些问题,我深入排查了微信读书 Web 端的渲染机制,并动手写了这个开箱即用的开源工具 —— weread-book-exporter。


二、技术复盘:为什么传统脚本会漏掉 40% 的字?

众所周知,微信读书 Web 端为了防止简单的 DOM 爬虫,正文字体并不是直接写在 HTML DOM 节点里的,而是通过 <canvas> 画布调用 CanvasRenderingContext2D.prototype.fillText 动态绘制出来的。

大多数基于 Playwright / Puppeteer 的抓取脚本,思路都是在页面初始化时 Hook fillText,然后不断模拟按下方向键 ArrowRight 翻页。

1. 罪魁祸首:上下滚动模式(isNormalReader)的虚拟画布分片

当浏览器处于默认的**上下滚动模式(isNormalReader)**时:

  • 微信读书会将一个章节渲染在一个高度达到 17,000px+ 的超长虚拟滚动容器里;
  • 但出乎意料的是,它并不会一次性把整章绘制到 Canvas 上,而是只绘制当前视口附近的 上半部分(约前 3,500~4,000 字);
  • 此时如果你直接触发 ArrowRight(下一章/下一页),阅读器不会替你向下滚动加载剩余分片,而是直接跳到了下一章的开头!结果就是每一章的下半截全部被静默丢弃。

2. 破局方案:强制锁定双栏水平模式 + 帧序列与分栏坐标切分

在 weread-book-exporter 中,我做了一套完整的底层改造:

(function() {
    window.__wr_chars = [];
    window.__wr_seq = 0;
    // 1. 拦截画布大面积重绘,递增帧序列号 seq
    var origClear = CanvasRenderingContext2D.prototype.clearRect;
    CanvasRenderingContext2D.prototype.clearRect = function(x, y, w, h) {
        if (w > 100 && h > 100) window.__wr_seq++;
        return origClear.apply(this, arguments);
    };
    // 2. 拦截文字绘制,同时记录文本、坐标、字体字重、左右分栏及帧序列号
    var origFill = CanvasRenderingContext2D.prototype.fillText;
    CanvasRenderingContext2D.prototype.fillText = function(text, x, y) {
        if (text && text.trim()) {
            var st = (this.canvas && this.canvas.getAttribute('style')) || '';
            var side = st.includes('right:') ? 1 : 0;
            window.__wr_chars.push({
                t: text,
                x: Math.round(x * 10) / 10,
                y: Math.round(y * 10) / 10,
                f: String(this.font),
                side: side,
                seq: window.__wr_seq
            });
        }
        return origFill.apply(this, arguments);
    };
})();

配合以下三步策略,实现了 100% 零漏字抓取:

  1. 自动检测并切换至双栏翻页模式(isHorizontalReader):在双栏模式下,每次按 ArrowRight 严格前进一个双栏视口(左页 + 右页),绝不跳过任何虚拟分片;
  2. 按 (seq, side) 原子化切分单页:过去很多脚本只按 y 坐标排序,一旦遇到左栏满页、右栏只有半页(或跨章重绘),左右两栏相同 y 坐标的字就会拼成同一行乱码。现在通过 seq(重绘帧)+ side(左/右栏)精准隔离每一单页,彻底根治乱序;
  3. 利用 ctx.font 还原原书排版语义:
    • >= 24.5px → 自动识别为章节/小节标题(支持跨行长标题自动合并);
    • 22.0px ~ 24.5px → 识别为核心引文 / 古籍条文;
    • bold 18px → 二次语义判别区分独立重点段落与行内加粗,并自动拼接被画布折行打断的句子;
    • 18px → 现代正文段落。

三、出版级多格式导出引擎:PDF / EPUB / Markdown 一键生成

抓取完完整章节与原书高清插图后,export_book.py 会一键生成三种高颜值格式:

1. 大字号「苹方 + 楷体」精排 PDF

专为平板批注与深度阅读设计:

  • 双字体黄金搭配:现代解读正文采用 13.2pt 苹方字体(PingFang SC) + 1.95 倍行距;古籍条文/核心引文采用 14.8pt 华文楷体 + 宣纸米底色卡片 + 朱砂红编号徽章;
  • 完整导览体验:自动生成封面页、双栏可点击跳转目录页、PDF 阅读器侧边栏章节书签大纲(Outline),以及带页码的页眉页脚。

2. 双字体标准 EPUB

  • 内嵌多字体 CSS 样式表与全部原书插图,在 Apple Books、Kindle 或阅读器中打开即享双字体排版。

3. 纯净 Markdown(直通 Obsidian / Notion)

  • 自动将引文转换为标准 > **引文** 引用块,去除多余 HTML 标签污染,完美适配 Obsidian 双链笔记与全文检索。

四、快速上手(只需两行命令)

1. 克隆仓库并安装依赖

git clone https://github.com/WANGXIN-567/weread-book-exporter.git
cd weread-book-exporter
pip install -r requirements.txt
playwright install chromium

2. 抓取并导出全书

在微信读书网页版打开你已购或会员期内的书籍,复制地址栏末尾的书籍 ID(即 https://weread.qq.com/web/reader/<book_id> 中的 <book_id>):

# 第一步:启动浏览器抓取全书章节与插图(首次运行扫码登录,状态自动持久化在本地)
python crawl_weread.py <book_id>

# 第二步:一键生成精排 PDF、EPUB 与 Markdown
python export_book.py <book_id>

五、开源地址与致谢

项目已在 GitHub 完全开源(MIT 协议): 👉 github.com/WANGXIN-567…

注:本项目仅用于个人学习、无障碍大字号阅读辅助与前端 Canvas 渲染技术交流,请支持正版阅读,仅对个人合法拥有阅读权限的书籍使用。

如果你也常在微信读书上看书、做 Obsidian 笔记或用 iPad 批注 PDF,欢迎去 GitHub 点亮一颗 Star ⭐,有任何建议或需求也欢迎提 Issue 交流!