Show

知乎备份脚本开发初探

缘起

咱一直有刷知乎的习惯,希望能借此足不出户认识更大的世界。虽然知乎上内容越来越水,不过只看关注、热榜、搜索,不看推荐的话还是可以接受的。咱也陆续关注了一些厉害的答主,增长了不少见识。

不过,随着社会上风气越来越不好,网络上审查越来越严,互联网逐渐没有记忆,知乎上很多有价值的内容因此被删,实在可惜。而且,互联网日益吵闹,在信息洪流之下,有用的信息稍纵即逝,很快就被大量的垃圾信息淹没。

23年12月开始构想写一个工具,方便浏览时随手存档,不过一直拖到月底才动工。

过程

首先,肯定要看看有没有现成的工具可以用,于是,咱在 GH 上搜索了一下,找到的多半是爬虫,不过,有一个工具感觉还可以:github.com/Howardzhangdqs/zhihu-copy-as-markdown

一键复制知乎文章/回答为 Markdown,下载文章/回答为 zip(包含素材图片与文章/回答信息),备份你珍贵的回答与文章。

他写成了油猴脚本,试了一下能凑合用,不过有很多 bug,功能也不能满足需要。

咱的大致需求是,在浏览知乎网页时,能够对目标内容进行保存,需要支持关注页等各种页面,支持各种内容。保存方式可以选择文本、图片、PDF等,最好能保存评论区。

这个项目还比较新,是 11 月创建的,去看了一下作者主页,似乎还是高中生,不敢相信。

把项目下载下来,发现用的是TS,咱虽然不太会,不过照葫芦画瓢还是没问题的。脚本大致原理就是不停遍历页面上的富文本,然后添加下载按钮,并且解析内容为Markdown文本和zip包。Markdown 相关功能比较复杂,是一套 lex,parse 之类的东西,实现的效果也挺好,感觉作者也是费了很大功夫的。不过咱发现程序竟然每秒都遍历并且自动解析一次页面上所有内容,感觉实在是疯狂,遂开始改写。

首先无限循环改为滚动才触发,并且添加防抖,然后解析时机改为点击按钮才开始解析。

ts
let timer: any = null
window.addEventListener("scroll", () => {
    //debounce
    if (timer) {
        clearTimeout(timer)
    }
    timer = setTimeout(main, 3000)
})

经过分析,梳理了需求,画出了简单的框图,然后开始繁琐的适配过程,知乎有很多不同类型的页面和内容,这些内容的 DOM 结构都不尽相同,比如关注推送页里,点赞的回答有作者栏,原创的回答却没有。

最终确定有 7 种场景,3 种类型,后面咱会依此编写getTitlegetAuthor等方法。原作者这里实现得很潦草,有不少 bug。

ts
    let scene, type
    if (window.location.pathname == "/follow") scene = "follow"
    else if (window.location.pathname.slice(0, 7) == "/people") scene = "people"
    else if (window.location.pathname.slice(0, 9) == "/question" && !window.location.pathname.match(/answer/)) scene = "question"
    else if (window.location.pathname.slice(0, 9) == "/question" && window.location.pathname.match(/answer/)) scene = "answer"
    else if (window.location.pathname.slice(0, 4) == "/pin") scene = "pin"
    else if (window.location.hostname == "zhuanlan.zhihu.com") scene = "article"
    else if (window.location.pathname.slice(0, 11) == "/collection") scene = "collection"
    else console.log("未知场景")

    if (getParent(dom, "AnswerItem")) type = "answer"
    else if (getParent(dom, "ArticleItem")) type = "article"
    else if (getParent(dom, "PinItem")) type = "pin"
    else console.log("未知内容")

截图

弄这个功能费了不少劲,由于浏览器没有提供js可用的原生方法,所以只能用 canvas 去模仿页面再截取。网上搜了一阵子,最终使用 modern-screenshot 这个库,不过有 bug,只好加类名用 CSS 修。

前置处理:更改黏性定位的元素定位,隐藏头像,复制图片文件名

想法

知乎想法看似简单,处理起来却一点也不简单,各种嵌套杂糅,不同样式的链接,文本间用<br>换行,咱弄了两个晚上才弄好。

成果

学到的技术

细节

暗坑:字符串转数字为 NaN:是因为里面有一个%E2%80%8B \u200B零宽空格

TypeScript

TypeScript 是对 JavaScript 的增强,为它提供类型支持。TypeScript 能够在写代码时对 js 进行类型检查,提前发现错误,避免程序运行时出错。同时,它也增强了编辑器的功能,提供了代码补全、接口提示、跳转到定义、代码重构等能力。

经过一段时间的使用呢,咱对 JavaScript 和网页里各种东西的类型有了更多的认识,也感受到了代码补全和类型提示带来的好处。


未完待续,其他有待补充

  • 知乎备份脚本开发初探
  • 作者:天雪酱  发布于:2024-01-04  更新于:2025-01-17  许可协议:若无特别说明,均为 CC BY-NC-SA
知乎备份脚本翻新
用 JS 在网页中飘落一年四季的特效