ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

知乎内容一键备份:Python实现本地多格式存档工具

知乎内容一键备份:Python实现本地多格式存档工具 1. 为什么你需要一个知乎内容备份工具年初我整理自己的创作素材时发现过去几年在知乎上写了几百条回答、上百篇文章还有很多收藏夹里的优质内容。当时想把它们全部整理成本地文档结果手动复制粘贴到怀疑人生——网页一篇一篇打开、选中、复制、粘贴、排版一篇长回答折腾下来十几分钟几百篇内容搞了整整一个周末也没弄完而且格式还乱七八糟。后来在整理过程中又发现一个更糟心的问题有几篇早期的回答已经被删除了。有些是自己觉得不成熟删掉的有些是盐选内容下架了还有一篇是早年随手答的、我自己都忘了内容结果被系统判定违规隐藏了。这些内容在网页端和App里都已经看不到了但我还记得当时写那篇回答时查了不少资料里面有些技术细节现在还想引用。那种“明明是自己写的内容却被平台吞了”的无力感非常糟糕。也是从那时候起我决定认真做一套知乎内容的本地备份体系把回答、文章、想法、专栏、收藏夹全部纳入备份范围统一导出为本地文件。这套工具从最初的一个脚本逐渐演进成可以一键跑完整个备份流程、生成四种格式文件的完整方案。很多在知乎创作超过一两年的朋友对“备份”这件事其实都有类似的需求只是没找到顺手的方式。先说下这个工具的核心能力它可以批量拉取你自己的全部回答、文章、想法、专栏和收藏夹内容导出为txt、word、html和pdf四种格式。支持增量备份只拉取新增或修改过的内容不用每次全量重跑支持内容去重还能处理知乎网页端反爬机制的干扰。整个备份过程只需要一条命令跑完后会自动按时间线归档。适合谁用首先是知乎的长期创作者几千条回答靠自己手动备份不现实其次是做内容矩阵运营的人需要把知乎内容同步到其他平台本地有一份干净的原始内容会方便很多再就是有内容洁癖、希望保存完整创作记录的人。如果你只是偶尔刷知乎、不产内容那这个工具对你帮助有限但如果你希望把知乎账号沉淀成一份属于自己的“数字资产”这篇文章值得看完。提示人家官方一般不会提供完整的内容导出功能。第三方工具能否长期存活存在不确定性这也是我坚持自己做一套本地方案的原因——平台政策再怎么变本地文件是实打实属于自己的。2. 工具选型与方案设计为什么选择这套技术栈2.1 总体架构设计整个备份系统拆成三个模块数据采集层负责从知乎网页端接口拉取内容处理登录态、频率限制、反爬校验数据存储层统一以 JSON 和 Markdown 作为中间格式存盘保留完整元数据导出渲染层把中间格式转换为 txt、word、html、pdf 四种最终产物这种分层设计的好处是各层解耦如果知乎接口变了只需要改采集层如果只想调整导出样式不需要碰采集逻辑。中间格式用 Markdown JSON 的组合既保证内容可读性又保留了发布时间、点赞数、评论数、编辑历史等元数据。2.2 技术栈选择与理由我用的是 Python 3.10 Requests BeautifulSoup Pandoc wkhtmltopdf 的组合。这四个组件各管一段Python 和 Requests 负责网络请求和基本逻辑控制不用过多解释生态成熟、社区案例多遇到反爬策略时能找到大量参考。BeautifulSoup 用来解析 HTML 结构。知乎网页端的回答正文和文章正文都是标准 HTML用 CSS 选择器就能准确定位标题、正文容器、标签、发布时间这些节点。相比正则表达式BeautifulSoup 容错性更好页面结构微调时不会立刻挂掉。Pandoc 是一个文档格式转换神器负责把 Markdown 转成 html 和 docxWord 格式。它能把 Markdown 里的标题层级、代码块、引用、表格完整映射成 Word 的对应样式比直接用 Python 的 python-docx 库一个个手动拼段落省太多事。wkhtmltopdf 把 HTML 渲染成 PDF。之所以不直接用浏览器打印是因为 wkhtmltopdf 支持自定义页眉页脚、页边距和 CSS 分页样式适合大批量生成统一风格的 PDF。工具对比表格方案优点缺点我的建议Pandoc格式转换质量高Markdown生态标准无法直接处理网络请求和反爬作为核心转换引擎python-docxPython生态原生无需额外安装逐段拼接太繁琐样式控制复杂只在需要特殊Word样式时用ReportLab纯Python生成PDF功能强API偏底层中文支持需单独配置不推荐成本高Playwright无头浏览器渲染最接近所见即所得资源占用大并发时容易出问题不适合大量内容wkhtmltopdf够用这套栈选完后的经验是能用成熟命令行工具处理格式转换就不要自己造轮子。Pandoc 和 wkhtmltopdf 这类工具经历了大量真实场景的检验边界情况处理得比我自己写代码细致得多。2.3 登录态与 Cookie 的处理逻辑知乎的内容接口需要登录后才能访问完整内容尤其是自己的回答列表和收藏夹接口。所以工具必须先完成登录态注入。我采用的是手工获取 Cookie 的方式先在浏览器登录知乎然后从开发者工具里复制 cookie 字符串粘贴到工具的配置文件中。这样做比脚本自动登录更稳定因为知乎的登录接口有多种验证方式验证码、短信、二维码自动化处理成本高且易失效手工复制 Cookie 只需要一次Cookie 没过期就能一直用。Cookie 的存储建议放进一个独立的 cookies.txt 文件不写死在代码里这样脚本上传到 Git 仓库时不会泄露隐私信息。注意Cookie 属于敏感信息拿到之后等同于账号的操作权限。工具仅用于备份本人内容。处理他人账号的 Cookie 很可能违反平台用户协议。3. 核心实现一键批量下载备份的完整过程3.1 备份范围与接口梳理知乎的内容类型可以分成五类每类需要调用不同的接口回答/api/v4/me/answers分页返回当前用户的全部回答文章/api/v4/me/articles同样是分页接口想法/api/v4/moments类似朋友圈的时间流内容专栏/api/v4/me/column-contributions会返回专栏名和文章列表收藏夹/api/v4/members/{user}/favorites只是收藏夹列表收藏夹里的具体内容还要再调一层接口这些接口返回的都是 JSON 格式里面包含了内容全文。比如回答接口返回的content字段是 HTML 格式的正文created_time和updated_time是 Unix 时间戳voteup_count是点赞数每个字段都有清晰的定义。接口调用有几个要点所有接口都支持offset和limit参数做分页limit建议设为 20太大容易触发频率限制返回的 JSON 里有一层分页信息包含is_end字段用它来判断是否还有下一页如果返回 403 或出现验证码页面说明当前 IP 被限制了需要降低请求频率或切换代理3.2 数据采集主流程采集主流程用了一个简单的循环对每类内容从头到尾遍历所有分页把每一条内容解析成统一的中间结构存入数据目录。解析逻辑的核心是把 HTML 正文转成 Markdown。这里直接用 Pandoc 包一层先用 BeautifulSoup 提取正文容器的 HTML然后调用 Pandoc 把它转成 Markdown。知乎正文里的代码块、图片、链接、粗体和引用都能被正确转换尤其是代码块Pandoc 会保留语言标记转出来的 Markdown 干净可读。一个典型的中间 JSON 结构长这样{ id: 123456789, type: answer, title: 如何看待xxx问题, content_md: 这是回答的正文使用Markdown格式存储..., created_time: 1680000000, updated_time: 1680003600, voteup_count: 1024, comment_count: 56, url: https://www.zhihu.com/question/123/answer/456, tags: [科技, 编程] }这个 JSON 文件就是本地备份的“母本”之后所有格式的导出都从它渲染而来。建议在数据目录下再按类型建子目录比如data/answers/、data/articles/每个子目录下按日期_id.json命名方便查找和排序。3.3 增量备份与去重增量备份是这个工具的亮点设计之一。每次跑备份之前工具会扫描本地已有的 JSON 文件收集已有的内容 ID然后只请求那些还没有备份过的新内容。具体做法是翻页时每拿到一条数据先判断它是否在已备份 ID 集合中是就跳过不是就下载。因为知乎的分页会按时间倒序排列一旦发现连续几条都已经被备份过大概率可以提前结束翻页。不过这里有个坑要提醒一下知乎的分页排序并不是完全稳定的。少数情况下第一次备份后新增的内容会和旧内容混在一起不同页之间可能有重叠。所以我的做法是先备份所有类型的所有分页只跳过已存在的 ID不做提前终止虽然数据量大时多跑几轮页面但换来的是完整性。如果你想追求速度可以加一个--fast参数启用连续 5 条已备份则提前终止的逻辑。增量备份后还有个麻烦如果某条回答被修改了本地存的还是旧版本。要解决这个问题可以在 JSON 里记录fetched_at采集时间每次运行后如果一条内容的updated_time比本地记录的fetched_at晚就重新拉取。这算是比较完整的“增量变更检测”方案了。3.4 完整的一键运行流程用命令行参数把整个流程串起来python zhihu_backup.py --all --export txt,docx,html,pdf执行流程如下读取cookies.txt按类型answers → articles → moments → columns → favorites依次遍历采集每采集一条内容立即写入 JSON 文件避免中途崩溃丢失数据所有类型采集完成后遍历本地 JSON 文件生成对应的 Markdown 文件调用 Pandoc 批量把 Markdown 转成 docx 和 html调用 wkhtmltopdf 把 html 渲染成 pdf最后生成一个备份索引页汇总本次备份的时间、内容数量、各格式文件路径整个流程在 500 条回答 100 篇文章 300 条想法的账号上实测约 8 分钟跑完其中大部分时间花在 PDF 渲染上。如果不导出 PDF只需要 3 分钟左右。3.5 收藏夹内层内容的采集收藏夹是五类内容里最容易遗漏的。外层接口只返回收藏夹的元信息名称、描述、收藏数里层还需要再请求每个收藏夹下的具体内容。知乎的收藏夹内容接口也需要分页遍历每页同样返回 JSON 数组。因为收藏夹里的内容可能是别人的回答或文章所以在备份时我用的是“外链引用”模式把收藏夹作为一种条目单独存 JSON里面记录来源链接而不是完整复制内容。这样既保留了收藏夹的结构又不会因为别人删了内容导致本地报错。实际使用中我发现收藏夹的含金量很高很多早期收藏的技术帖已经被删了但我通过收藏夹里的 URL 标题多少还能回忆起当时的标题和方向。如果当时没有备份收藏夹索引这些痕迹就彻底没了。4. 从 Markdown 到四种格式多格式导出的技术细节4.1 为什么用 Markdown 作为中间格式很多人会问为什么不直接抓 HTML 存下来或者直接调接口拿到什么就存什么我坚持用 Markdown 作为中间格式原因有三个纯文本可读即使没有任何渲染工具用记事本也能直接读内容未来十年二十年也不会打不开格式转换友好Markdown 的结构化程度刚好能承载标题、列表、代码块等语义主流转换工具Pandoc 等对它的支持最完善方便二次编辑备份内容如果还要发到公众号、博客、其他平台Markdown 是最通用的编辑中间格式相比之下直接存 HTML 文件虽然所见即所得但多年以后可能带着一堆过时的样式标签直接存 JSON 则没法直接人读。4.2 导出 txt最朴素的纯文本格式txt 导出最简单但有一个坑字符集和换行符。知乎的中文内容必须用 UTF-8 编码保存否则在 Windows 记事本里会乱码。而 Python 的open()函数在不同平台默认编码可能不同所以一定要显式指定with open(file_path, w, encodingutf-8) as f: f.write(markdown_content)Windows 记事本对 UTF-8 无 BOM 的文件编码识别其实还比较宽容但如果你要给别人传文件最好统一保存为 UTF-8 with BOM 或转成 GBK避免对方用老旧工具打开时乱码。我实测下来Windows 11 的记事本已经能自动识别 UTF-8 无 BOM 了但某些第三方编辑器仍可能有显示问题。稳妥做法是加一个导出选项--txt-encoding utf-8-sigutf-8-sig会在文件头加 BOM兼容性最好。txt 的排版建议每条内容前加三行元信息标题、发布时间、原文链接然后空一行再放正文。如果是回答标题可以取对应的问题名。注意正文里的换行要保留Markdown 转纯文本时不要过度合并空行否则阅读体验会很差。4.3 导出 Word用 Pandoc 实现高质量 docxWord 导出直接调用 Pandocpandoc input.md -o output.docx --reference-doccustom-reference.docx其中--reference-doc参数指定一个 Word 样式模板文件。这个模板决定了最终 Word 文档的字体、字号、标题样式、代码块样式。默认的 Pandoc Word 模板一般般建议自己调整一次。做法是先用 Pandoc 生成一个默认参考文档然后在 Word 里修改样式保存后再供给工具pandoc -o custom-reference.docx --print-default-data-file reference.docx然后打开这个 custom-reference.docx修改“正文样式”的字体为宋体五号、标题样式改为中文黑体、代码块样式改为 Consolas 等保存后放回工具目录。还有一个细节知乎正文里的图片链接指向的是picx.zhimg.com这类 CDN 地址。如果直接转 Word图片不会自动下载嵌入文档里会只剩下图片链接阅读体验差很多。我实测有两个方案一是用--resource-path配合脚本先把图片下载到本地再用 Pandoc 转 Word 时自动引用本地图片路径二是直接改用 HTML 中间格式Word 也原生支持 HTML 粘贴不过用 Pandoc 转 docx 配合本地图片路径的方案最干净。具体做法是采集时下载图片到assets/目录把 Markdown 里的图片链接替换为相对路径然后 Pandoc 会识别相对路径并嵌入 Word。注意图片总大小很大时docx 会膨胀到几百MB建议导出 Word 前先用脚本压缩图片或者提供--skip-images参数跳过图片嵌入。4.4 导出 HTML保留完整样式和交互HTML 导出最适合浏览器阅读和分享。Pandoc 支持生成 standalone HTMLpandoc input.md -o output.html --standalone --metadata title文章标题--standalone参数会生成一个完整的 HTML 文档包括html、head、body结构和内嵌 CSS不需要依赖外部样式表。这意味着单个 HTML 文件可以被任何浏览器直接打开也能方便地扔到微信、邮件、笔记软件里预览。生成的 HTML 默认样式比较朴素建议加一段自定义 CSS统一调整页面宽度、字体、代码块配色、引用块边距。我自己常用的 CSS 片段大致是正文最大宽度 720px 居中显示行高 1.7代码块背景色#f6f8fa引用块左边框 4px 灰色。还有一个思路把 HTML 做成类似知乎原生的排版风格模拟原站的卡片式布局让备份内容“看起来和知乎一致”。这个适合追求还原度的朋友不过需要额外写不少 CSS不是必须的。4.5 导出 PDF一字不差的中文排版PDF 导出的链路是Markdown → HTML → PDF。先转为带样式的 HTML再用 wkhtmltopdf 渲染成 PDFwkhtmltopdf --encoding utf-8 --margin-top 15mm --margin-bottom 15mm --margin-left 12mm --margin-right 12mm --footer-center [page]/[topage] --footer-font-size 9 input.html output.pdf关键参数里--footer-center可以加页码--margin-*控制页边距避免中文内容被截断。如果内容有多页还可以加上--page-size A4统一纸张大小。wkhtmltopdf 对中文的渲染依赖系统字体。Linux 环境需要安装中文字体比如 Noto Sans CJK SCWindows 一般自带微软雅黑不会有大问题。如果 PDF 里出现方块豆腐块九成是字体缺失安装对应字体即可。测试结果一个包含代码块的 5000 字长文生成 PDF 约耗时 5 秒大小约 1MB。这个速度和大小都在可接受范围内唯一要注意的是 wkhtmltopdf 对最新 CSS 特性的支持不如现代浏览器如果你在 HTML 里用了flex或grid布局渲染可能会错位。这也是我坚持在 HTML 导出和 PDF 导出用两套 CSS 的原因浏览器用华丽版wkhtmltopdf 用保守版。4.6 四种格式的适用场景总结格式适用场景特点txt长期存档、全文本搜索体积最小格式最简永久可读Word二次编辑、投稿、打印样式可控审阅批注方便html浏览器阅读、网页分享单文件自包含样式丰富pdf分发、归档、打印不可篡改跨设备一致我的建议是本地长期存档用 txtpdf需要编辑加工用 Word想要快速浏览和传播用 html。四套全导出也不费太多时间既然都跑了就一次导全。5. 实操踩坑记录知乎反爬、分页失效与样式丢失5.1 知乎反爬机制的第一轮“亲密接触”第一次用脚本批量拉取时跑了不到两百条就被知乎的验证码页面拦住了请求返回的 JSON 变成了一坨 HTML 验证页。调试之后发现两件事一是同一 IP 的请求频率检测比预想严格二是 User-Agent 太容易被识别。解决频率限制的办法很简单在每次请求之间加随机延时延时范围设在 2~5 秒之间。这个区间既能显著降低触发概率也不会让整体耗时太过夸张。2000 条内容的完整备份大约会增加 1.5 小时的等待时间为了数据完整性还是值得的。User-Agent 也必须伪装成真实浏览器不能带python-requests字样。直接从 Chrome 浏览器的“关于”页面复制完整的 UA 字符串填入 headers。注意不是只改 UA 就能万事大吉知乎还会校验 Cookie 的完整性如果 Cookie 缺失或过期UA 再真实也会被拦。另外我强烈建议开启重试机制请求失败后自动等待 10 秒再重试最多重试 3 次如果重试还是失败跳过当前内容并记录日志不要中断整个备份流程。这样一个 2000 条的备份跑下来可能有 5 条内容因为网络抖动没拉全但整体备份流程不会崩。5.2 分页接口的一个隐蔽问题知乎的分页接口偶尔会出现返回空数组但仍然提示“还有下一页”的情况。如果直接按is_end判断就可能陷入死循环。我的判断逻辑后来改成了“多条件组合”while offset total: data fetch_page(offset) if not data.get(data): break items data.get(data, []) if not items and retry_count 3: retry_count 1 time.sleep(10) continue if not items: break # 处理正常数据... offset len(items)核心思路是连续多次空返回就视为分页结束不再无脑翻页。同时记录实际拿到的条目数对照接口返回的total字段收尾时检查数量是否大致吻合不吻合就在日志里告警。另外知乎的分页接口对limit参数是有上限限制的设置 20 是安全的设置 100 实测会被直接 400 拒绝。这也是我推荐 20 的原因不是怕频率限制是接口本身的边界。5.3 多层级引用内容不完整收藏夹里的内容除了转发的回答偶尔还有用户自己的想法、文章、视频。我在初期版本里只抓了回答和文章两种类型导致一部分收藏内容无法展示。解决方法是给收藏夹内容加一个“类型字段”在 JSON 里记录每条收藏内容的类型answer、article、moment、video然后为视频和想法单独写解析逻辑。视频类内容没有可转存的正文我就在 JSON 里存视频标题、封面 URL、视频播放页链接导出时以链接形式呈现。5.4 Word 表格样式丢失如果你的知乎内容里包含表格默认 Pandoc 转 Word 的表格样式可能很丑——没有边框单元格间距奇怪整个表格看起来像没有格式化一样。解决方案是在参考文档custom-reference.docx里为“Table”样式设置边框、行高和单元格边距。具体操作在 Word 里创建一个带边框的表格右键表格样式修改成你想要的样式保存为参考文档即可。改过一次之后后续所有 docx 里的表格都会沿用这套样式。5.5 图片下载的防盗链处理知乎的图片 CDN 有防盗链机制直接下载有时会返回 403。原因是缺少 Referer 头。解决办法是在下载图片时把 Referer 设为https://www.zhihu.comheaders { User-Agent: user_agent, Referer: https://www.zhihu.com }加了 Referer 之后正常情况下图片就能正常下载。另一个坑是部分图片的 URL 带?r...之类的动态参数保存文件名时要处理一下避免文件名里出现问号等非法字符。5.6 增量备份的排序问题增量备份时如果只靠分页顺序判断“已备份的都靠前”可能会因知乎分页不稳定而漏掉部分内容。我在实测中发现回答列表偶尔会出现顺序微调导致某几条内容被跳过去。稳妥的做法是先拉取所有分页的数据哪怕慢一点在内存里按 id 去重然后把与本地已有 id 相同的内容丢弃剩下的全部保存。这样虽然每一轮都会把所有分页都拉一遍但不会漏内容。对你关心的“速度”问题如果内容特别多也可以用折中方案只在新增数量多的时候触发全量分页扫描平时用提前终止模式。6. 备份数据的长期管理与维护建议备份不是跑完一次就完事的。工具做出来之后长期维护要考虑数据怎么归档、怎么验证完整性、怎么防止备份文件本身丢失。我目前的目录结构是这样的zhihu-backup/ ├── cookies.txt ├── config.json ├── data/ │ ├── answers/ 按日期_序号.json │ ├── articles/ │ ├── moments/ │ ├── columns/ │ └── favorites/ ├── exports/ │ ├── txt/ │ ├── docx/ │ ├── html/ │ └── pdf/ └── logs/每个类型目录里的 JSON 是原始数据exports 目录下的四种格式只是渲染产物随时可以重新生成。完整性验证我采用两套维度一是数量维度每次备份结束后统计 JSON 文件数、导出文件数和自己账号里实际的内容数核对二是内容维度随机抽几篇导出文件人工看一眼格式是否正确、图片是否缺失。数量校验可以用脚本自动做内容校验就只能靠抽查了。备份的“备份”我建议把整个数据目录压成一个 tar.gz 文件然后存到网盘或移动硬盘。数据存储介质都会损坏本地磁盘也一样重要内容至少要有两份副本。我目前是每季度手动压缩一次放到一个专门做冷备份的移动硬盘里成本很低但心里踏实。数据刷新频率方面如果只是个人存档每月跑一次就够了。如果是内容运营每周跑一次比较合理评论数和点赞数的变化值得追踪。7. 再扩展一步备份后的内容还能怎么用备份完成不等于结束格式都导出来了这些文件的用途其实比想象中大得多。如果你把自己的回答、文章按时间维度铺开就是一份个人创作编年史。我去年整理时把历年写的所有技术文章丢进本地的全文搜索工具比如 Everything 或 ripgrep想找某段脚本、某个解决办法一搜就到比在知乎站内搜索还靠谱——因为知乎的搜索结果受推荐机制影响不一定把你自己的内容排在前面。如果有多平台分发需求Word 版本可以直接用于公众号排版前的内容整理txt 版本能快速导入各类笔记软件HTML 版本可以直接作为邮件正文使用。我自己试过把备份的 HTML 直接粘贴到公众号编辑器里样式基本能保留。还有一个进阶玩法把 JSON 数据导出来做内容分析。比如统计自己哪个月创作量最高、回答的平均长度变化趋势、哪个类型的内容获得的点赞转化率最高。有了结构化数据这类分析做起来很简单。工具导出的 JSON 本身就是结构化数据稍加处理就能做成图表。对于特别早期的内容如果你发现某些回答的排版和表达方式已经过时但内容本身还有价值可以把它们提取出来修改后重新发布成文章。我开始做内容整理后好些旧回答被翻新成了长文反响不错。这大概是本地备份计划里最直接的“变现”途径了。8. 代码是开源的但请自己掌控关键环节整个工具的功能其实不复杂核心代码量大约在五六百行认认真真读一遍完全能自己维护。我不建议直接跑一个看不懂的脚本尤其是它的 Cookie 和账号权限直接挂钩。几个安全底线不要把cookies.txt提交到公开仓库不要把数据目录传到公开网盘脚本运行前看一下它做了哪些网络请求最好自己过一遍代码只在本地环境运行不要在在线代码运行平台里跑知乎的接口结构可能随时调整。如果你发现工具某个环节失效了先去浏览器里的开发者工具看一眼对应接口的实际返回然后针对性地修改解析逻辑。只要抓到的前端页面还能正常展示内容这套工具的调整思路就可以一直复用。最后分享一个我的使用习惯每隔一段时间我会把备份的 PDF 文件用手机打开翻几下。看到几百篇回答整整齐齐地躺在本地那种“这些内容的控制权在我手里”的感觉比任何云端的“已同步”都来得实在。
返回列表