Jupyter 笔记本转纯文本 — 免费、私密

在浏览器里把 .ipynb 笔记本转成纯文本。剥掉 JSON 外壳、内嵌图片和单元格元数据,只留下代码和文字。

Jupyter Notebook 转纯文本:只取内容,不带脚手架

当下游对格式没有任何主张时,纯文本才是正确的目标。一条 grep 管道、一次 diff、一个搜索索引、一次查重、一封发给不想收附件的同事的纯文本邮件——这些都不会因为反引号和井号变好,其中好几个反而会被这些「对渲染器有意义、对读者毫无意义」的标点弄糟。

这个转换器接收 .ipynb,把文字和代码原样交还,不添加任何标记。单元格之间用空行分隔。代码就是代码,不加围栏。打印出来的结果出现在产生它的单元格下方,标一个朴素的 Output:。JSON 格式包在这些内容外面的一切都被丢掉。整个过程在这个浏览器标签页里完成。

关于 token 的那笔账

把 notebook 展平最常见的理由,是下游按 token 收费,而原始 .ipynb 是一种极其昂贵的表达方式——说得很少,花得很多。这个比例值得说具体些。

一个三十个单元格、其中一半会画图的 notebook,落到磁盘上动辄两三兆字节。里面的文字和代码可能只有八千字节。其余全是 base64 图像数据、同一结果在纯文本之外多出来的 HTML 副本,以及逐单元格的账目信息。把原始文件喂给模型,代价不只是比应有的高一百倍——它通常直接失败,因为上下文窗口被不携带任何信息的字符填满了。

更糟的是 base64 的分词效果极差。普通英文平均大约四个字符一个 token。一段随机 base64 没有可供分词器词表压缩的重复子串,于是接近两个字符一个 token。文件里最没用的那部分,恰好也是每字符 token 数最高的那部分。上面的计数器会显示结果的 token 数,你能直接看到自己真正发出去的是多少。

展平之后还剩下什么

去掉结构不等于去掉意义,而这条界线正是这类工具成败的地方。保留的部分:

  • markdown 单元格的内容,原样保留。#** 这些字符会留下,因为在 markdown 单元格里它们是作者表达强调和层级的唯一手段。把它们剥掉,等于把一份本来就是散文的文档压成一团没有区分的文字。
  • 代码,一字不动。缩进保留,注释保留。Python 的语义取决于行首空白,所以这不是审美选择。
  • 文本形式的结果。打印输出、返回值、DataFrame 预览的纯文本形态。
  • 错误类型、错误信息和回溯,并去掉终端颜色码。
  • raw 单元格,里面通常是作者不希望被动过的 LaTeX 或 reStructuredText。

丢掉的部分:图像和视频负载、PDF 附件、任何同时存在纯文本形态的结果的 HTML 孪生体、执行计数、单元格标识、空的元数据对象,以及完全空白的单元格。一个只有空白、也没有输出的单元格,不是任何人需要读的东西。

图片占位符要不要留

图被删掉之后,要不要留个记号,这是个真实的权衡。什么都不留,文本读起来干净,但「如上图所示」这样的句子从此指向虚空。留个占位符,你就多了一行几乎不携带信息的内容。

默认留一行短短的、注明媒体类型的占位,上面的开关可以关掉它。还有一个相关的细节值得知道:matplotlib 在输出图像的同时还会给出一个文本表示,内容永远是类似 <Figure size 640x480 with 1 Axes> 的东西。如果照单全收,你会先后得到这一行和占位行,同一件事说两遍。所以当一张图被丢弃时,这条特定的文本会被一并压掉。

从中间截断

有些输出长是因为它信息量大,有些长是因为里面有个循环在打印。工具分辨不出来,所以它给每段输出设上限,并从中间下刀,保住开头和结尾,同时标出中间少了多少字符。

从中间截断胜过另外两种做法,理由很具体:一段长输出里最有信息量的两处,几乎总是开头——它显示产出的形状,和结尾——它显示最后落在哪里。掐尾的 DataFrame 让你看到前几行、却藏起汇总行;掐头的则藏起列名。从中间切,故事的两头都在。

展平后的 notebook 能干什么

JSON 一走,好几件事就变简单了。用纯文本对比两个版本的 notebook,看到的是分析本身的变化,而不是哪个执行计数加了一——nbdime 之所以存在,正是因为普通的 git diff 作用在 .ipynb 上根本没法读。在一堆 notebook 里找出画某张图的那个函数,变成一次普通的 grep。给报告统计字数、把 notebook 喂给语音合成、把方法学部分粘进一个不渲染 Markdown 的文档里,都不再需要额外清理。

隐私,以及它在这里为什么是结构性的

Notebook 是工作文档,而工作文档会攒下没人打算留着的东西:调试 API 时粘进去的令牌、一条连接串、为了验证 merge 而打印出来的二十行客户数据。把它交给一个转换服务,就是把里面的一切一并交出去。

这里的解析器是跑在这个标签页里的 JavaScript。你的文件由浏览器读取、在内存里转换、然后显示出来。没有上传,所以没有需要你信任的留存政策,也没有需要提交的删除请求。如果你更想要围栏代码块和 Markdown 标题,notebook 转 Markdown 工具在同样的保证下做这件事。