前一篇博文提到了最近我正在写一个把聊天记录读写抽象出来的库gchdb,刚才把库 publish 了,使用了 Rust 编写,并基于这个库写了个把 QQ 导出的 mht 文件导出到数据库的小程序ChatImporter。
出于方便备份的考虑,所有聊天记录都保存在单个 SQLite 数据库中,对于 SQLite 的使用我也算比较熟了,主要是因为公司有个小项目使用了 Rust 做后端,其中订单数据储存是用的 SQLite,碰到了不少的坑,至于为什么要用 SQLite,这就不得不赖 PHP 这个破烂了,以后有时间再写写 PHP 有多坑(咕),这里一笔带过。gchdb 的代码量不多,行数也就 1k 都不到,主要碰到的问题就是大量插入太慢,对需要的表做一下索引就好了,我把以前备份的 mht 全部导入到一个数据库里,大概每秒 1k 条左右(包括图片语音之类的 blob 的插入和关联)。
ChatImporter 现在只支持了 QQ 备份出来的 mht 格式的导入,不知道新版 QQ 的格式有没有变动,不过我现在已经不用 QQ 了,把存量聊天记录备份了就完事了。mht 全名 mhtml,是由rfc 2110和rfc 2557定义的,然后这俩说明了格式是基于rfc 822的,这个 rfc 822 就是平时发送电子邮件所用的格式(eml,Gmail 里在邮件菜单选择“显示原始邮件”所看到的内容就是一个 eml),虽然后面由更新了几个版本,不过就是改了些 headers 和定义什么的,整体格式没什么大变化。很幸运的是 eml 的解析库有人用 Rust 写了,这里我选了mailparse这个库。到这里性能还是可以的,100m+的 mht 解析一下也才 100ms 左右,但是之后解析 html 会很慢,QQ 的图片散而多,发视频的人比较少,能把体积弄到 100m 以上,本身的聊天记录数量也是不少的了,这里我解析 html 用的是scraper,主要特点是可以用类似 css selector 的语法去 select 节点,但是因为内部的节点用了个 Cell,没法跨线程 move,导致 rayon 也没法用了,只能单线程去 parse,还好聊天记录也不多,总量 3G 多的聊天记录摆在那导入,出门吃个饭回来已经导入完成了,反正只是一次性工作,不优化了。
之后还计划支持 Android 和 iOS 的短信备份,以前经常用短信,囤了好几万条了,又不舍得删,比较蛋疼,至于微信,虽然是因为这破玩意才开这个坑的,但是仔细看了下,除了少数一些好友和群,其他都是可有可无的,不备份也不会少块肉,所以这玩意的导出就放到最后吧,或者干脆不备份了,反正无所谓。Part3 就等写完短信备份再来更新下吧。