怎么用 AI 编程把 248 篇 CSDN 博客搬回本地?

作者:程序员罗尼 | 发布:2026-03-16 | 原文:https://mp.weixin.qq.com/s/AZQnSLCyt721cXzPqI2EXw


本篇是一个「折腾记」:怎么用 Playwright + Node.js + Cursor,把我在 CSDN 上的 248 篇文章,完整、干净地备份成本地 Markdown


一、为什么要自己「搬家」

简单说三个动机:

  • 把内容掌握在自己手里:平台风控、改版、甚至关闭服务都不受我控制,本地 Markdown 永远在。
  • 方便二次创作:Markdown 靠 grep 和脚本就能各种分析、改写、汇总,很适合做自己的「知识库」。
  • 可迁移性:以后想搬到博客园、Notion、Obsidian,甚至自建博客,只要数据结构干净,迁移成本就很低。

目标就是一句话:

把我 CSDN 账号下的所有文章,一篇不少地变成本地 .md 文件,文件名有序、可恢复、可重复运行。


二、第一步:最朴素的 Playwright 爬虫

技术栈:

  • Node.js + npm
  • Playwright:模拟浏览器,打开页面、跑 JS
  • Turndown:把 HTML 正文转成 Markdown

第一版脚本只做了几件非常直接的事:

  1. 自动打开自己的博客主页;
  2. 找出页面上所有指向文章详情的链接;
  3. 对每个链接,自动打开详情页,提取正文内容,转成 Markdown,存成一个独立文件。

这一版在「少量测试文章」上跑得很顺,但一上量,问题就来了。


三、第一次撞墙:403 + 安全验证

当我开始批量抓时,发现:

  • 主页和文章页时不时返回错误提示;
  • 页面被「安全验证」界面替代,让你点验证码、做人机验证;
  • 脚本抓到的页面里根本没有正文,只有各种提示信息。

对应的改进有几层:

1. 伪装成「正常浏览器」

  • 使用和日常浏览更加接近的浏览器标识和语言设置;
  • 改用有界面模式,让页面真实地在屏幕上打开。

2. 把「登录 + 安全验证」交给人来做

脚本逻辑里专门加了一步:

  1. 打开个人主页
  2. 终端提示:「请在弹出的浏览器中登录 CSDN,登录完成后按回车继续…」
  3. 后续所有抓取都在这同一个登录会话下进行

这样,最敏感的那一步交给人去搞,后面脚本只重复「打开页面 → 取内容」。

3. 加「限速」防封

  • 每篇之间随机 sleep 1–3 秒
  • 后面又扩展成分批抓:每批 N 篇,中间大休息 2–3 分钟

这一轮之后,稳定性提升很多,试验了 10 篇,没有再被拦截,但还是觉得可以再安全一点,于是开始考虑用「官方 API + 浏览器」的组合拳。


四、发现列表接口:先拿清单,再抓正文

在翻各种前端请求时,我发现了一个给页面用的文章列表接口,可以按页返回账号下的文章信息(包括标题、简介、文章地址等)。

于是我把主逻辑改成了两段式:

  1. 列表段:通过这个接口分页拉,拿到账号下的全部文章清单(我这边大概是两百多篇);
  2. 正文段:对每篇文章的详情地址,用浏览器打开,按前一节的方式提正文,再转成 Markdown。

同时,我加了一些「工程化」的保护措施:

  • 可以只抓前 N 篇,便于测试和回归;
  • 可以从某一篇之后开始抓,避免重复;
  • 可以按「每批多少篇、批间休息多久」来控节奏;
  • 接口调用加了简单的重试逻辑,网络一抖动就退避几秒再来。

这一版在一开始跑得很顺,我也确实抓下来了七十多篇

但很快又踩到一条更隐蔽的风险策略:从某个阈值之后,后面的文章开始陆续访问不到,接口依然能给出列表,但详情页会被挡住,等于「列表有了,正文拿不到」,整套方案又不够稳了。


五、更加「人类」的版本:列表页 + 新 tab 抓取

为了进一步贴近人工行为,我又写了一个脚本 scrape-csdn-by-list.js,流程是这样的:

  1. 打开列表页,等我手动登录
  • 打开个人主页
  • 等我在浏览器手动登录、完成可能的人机验证,按回车继续
  1. 慢慢往下滚,直到页面把能加载出来的文章都展现出来
  • 不再翻传统意义上的「下一页」,而是像平时刷信息流那样,一直往下滑;
  • 每次滚动后稍微停一停,让页面有时间把更多文章加载出来;
  • 当连续几次滚动后文章数量都不再变化时,就认为这一屏已经刷到底了。
  1. 从页面里收集所有文章链接
  • 把当前页面里属于自己账号的文章链接全部提取出来;
  • 做一次简单的去重、过滤,只保留真正需要备份的那一批;
  • 记录「上一次抓到的最后一个 ID」,本次只从它之后开始。
  1. 逐条「点开」文章:新标签页 → 抓 → 等 → 关掉
  • 对每一篇文章:

  • 先随机等 1–3 秒,再打开一个新标签页;

  • 在新标签页里等正文加载出来,提取内容;

  • 再随机等 1–3 秒,把这个标签页关掉;

  • 然后继续下一篇。

整个行为模式非常接近一个真实的人在浏览自己的文章:下拉、点开一篇、看一会儿、关掉、再点下一篇

不过实际跑下来,这一步也遇到一个问题:

  • 人用浏览器访问时,看上去是可以无限下拉的列表
  • 但脚本实际执行的时候,看到的还是按页切分的视图,行为不一样。

所以光靠「滚动 + 列表页」依然拿不到真正的全量文章清单,于是我才有了下一步的兜底方案。


六、兜底:保存后台页面,离线解析出 248 篇

为了彻底搞清楚自己到底有多少篇文章,我选择了一个更「笨但靠谱」的做法:

  1. 用浏览器打开后台的文章管理页,把整页完整保存成一个本地 HTML 文件;
  2. 写一段小程序,专门去读这份 HTML:
  • 把里面所有指向文章详情的链接和文章 ID 找出来;
  • 做一遍去重、排序,最终生成一份纯文本的「文章 ID 清单」。

这一步的结果很惊喜:从离线 HTML 里一共解析出 248 篇文章,甚至比线上接口拿到的数量还多一些。

这也意味着:就算将来线上接口完全不可用,只要还有这份 HTML,我依然知道自己所有文章的 ID 列表。


七、最终形态:单篇小程序 + 逐条「点名」抓取

拿到完整的 ID 列表之后,我做了最后一次拆解,把任务分成两个非常简单的小模块。

1. 「只抓一篇」的小程序

这个小程序只做一件事:根据文章 ID 抓一篇文章,并保证文件命名有序。

它会:

  1. 从一个状态文件里读出「下一个编号」(没有就从 1 开始);
  2. 拼出这篇文章的详情地址;
  3. 用浏览器自动打开页面,提取正文内容;
  4. 保存成 编号-标题-文章ID.md 这样的 Markdown 文件(比如 12-某篇文章标题-12345678.md);
  5. 在文件头写入标题、原文地址、ID、编号等元信息;
  6. 把「下一个编号」加 1,方便下一篇接着用。

如果发现本地已经有这个 ID 对应的文件,就直接跳过,支持中断后继续跑。

2. 「负责读一行、调一次」的小助手

第二个小工具就像一个勤勤恳恳的助手:

  • 从那份 ID 清单里,逐行读出每个文章 ID;
  • 每读到一行,就执行一次「只抓一篇」的小程序;
  • 打印当前进度、简单日志;
  • 如果某一篇失败,就间隔一段时间后自动重试几次;
  • 成功抓完一篇后,再随机停几秒,防止触发新的风控。

这样一来,我既实现了「脚本一次只抓一篇、但可以连续跑完两百多篇」,又不用真的人肉敲两百多行命令。

到这里,整个「从平台到本地 Markdown」的链路就闭合了:

  • 可以通过登录 + 轻微的人工参与,绕过各种安全验证;
  • 可以用接口和页面拿到「尽可能完整」的文章清单;
  • 可以用离线 HTML 兜底,确保文章 ID 不会遗漏;
  • 最终用「单篇小程序 + 行为小助手」,稳稳地把两百多篇文章一篇篇落到本地。

八、最后的成果与反思

成果:

  • 全部 248 篇 CSDN 文章,都有对应的 .md 文件
  • 文件名:编号-标题-文章ID.md
  • 统一的 YAML front matter
  • 所有脚本都是可重复运行的:中间崩了、网络断了、验证不过,都可以重启继续,已有的文件不会被重复抓。

几个小反思:

  1. 对内容拥有掌控权很重要: 平台随时可能变,但 Markdown 文件和脚本在手,迁移自由度就大得多。
  2. 自动化不等于「全自动」: 在有 WAF(Web 应用防火墙)的网站上,适度引入人工步骤(登录 + 一次性验证)反而能让自动化更稳定。 登录这一步其实本来也不太适合追求「全自动」。
  3. 把任务拆成很多小脚本,是一种工程思维: 从「一键抓所有」到「API 列表 + DOM + 本地 HTML + 单篇脚本」,每一块都职责单一、可测试、可替换,这样系统的韧性更强。
  4. 重复使用的工具,才是最值钱的代码: 现在这些脚本已经变成一套「通用的 CSDN 文章导出工具」,以后如果我想再备份别的账号、或者做一些统计分析,只要稍微调整参数就行了。

如果你也在 CSDN、知乎、简书写了很多年,也许可以考虑给自己的内容做一份这样的「底层备份」——不是为了反抗平台,而是为了给自己多一层自由。


最后补一句,这次折腾里,绝大部分体力活其实都是交给 Cursor 来做的

我负责提想法、定大方向,遇到风控、脚本挂掉的时候,主要是跟它一起和错误日志「对线」、反复改 bug。

这次的体验也让我觉得,Playwright 这类自动化工具配合 AI,其实打开了很多新思路——以后不只是备份博客,很多「需要人肉刷页面」的事情,都可以考虑用「浏览器自动化 + AI」来尝试更多新玩法。

Let's play with AI

  • 本文采用「人言兑.md」自动排版,主题: 山吹 -
END