ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

让有道云笔记走出围墙的三个暗接口:youdaonote-pull 备份逆向手记

2026/8/27 16:00:18 拓冰建站 浏览量
让有道云笔记走出围墙的三个暗接口:youdaonote-pull 备份逆向手记 让有道云笔记走出围墙的三个暗接口youdaonote-pull 备份逆向手记【免费下载链接】youdaonote-pull 一个一键导出 / 备份「有道云笔记」所有笔记的 Python 脚本。 A Python script to export/backup all the notes of the Youdao Note.项目地址: https://gitcode.com/gh_mirrors/yo/youdaonote-pull有道云笔记网页端把「导出全部笔记」的入口拿掉后账号里十年的资料变成了一块碰不到的数字资产。youdaonote-pull 干的事就是把这块资产搬出来跑一遍脚本完成有道云笔记备份所有笔记按 Markdown 导出到本地让数据第一次真正脱离平台围墙。它凭什么拿到你的笔记第一次跑通脚本落盘的第一批文件根本不是 Markdown而是一堆 XML 垃圾字符的.note文件。现在回头看这是好事先把原始字节完整存下来转换是第二步的事。整条数据流其实只有一行cookies.json→requests.Session→ 根目录接口 → 文件列表接口 → 下载接口 → 原始 XML/JSON → Markdown → 图片链接迁移。入口是本地手写的cookies.json里面存三个从浏览器复制的键YNOTE_CSTK、YNOTE_LOGIN、YNOTE_SESS。脚本读进来逐条写进会话服务器从此把这个进程当成一个已登录的浏览器。YNOTE_CSTK还会被复用成cstk令牌拼进每个请求的 query 里是接口每次都要验的那道关。接口层本身只有三次调用getByPath拿根目录 IDlistPageByParentId按目录递归列文件最后sync的download方法取单篇笔记的字节流。三条 URL 和请求封装都收敛在 core/api.py 里读完这一个文件整个取数环节就没有黑盒了。笔记字节流不是人能读的「文档」类型是 XML新格式是 JSON。转换规则在 core/covert.py思路是把节点名直接派发成convert_节点名_func方法JSON 按类型字段派发同理。兜底策略也值得学遇到没登记的节点只保留纯文本转换失败时内容至少不丢。笔记的真身XML、JSON甚至还有 HTML脚本判断类型的方式朴素到可爱下载后偷看前几个字节?xml开头算 XML{开头算 JSON。还有一类老古董——2017 年以前新建的笔记其实是 HTMLXML 解析器直接抛ParseError捕获后换markdownify再走一遍。三种来源、一条出口这是这类工具最容易被忽略的兼容面。我是怎么把接口挖出来的先走官方 Open API走到死胡同第一版方案根本不是逆向。有道云笔记有官方 Open API文档多年没更新脚本写到一半才发现连 Markdown 文件的接口都没有。官方路断了才有后来这一套。浏览器开发者工具里蹲出的三个接口于是打开网页版F12 进 Network 面板点几个目录前端发给/yws/api/personal/的请求全暴露了。值得封装的就三个getByPath按路径取根目录信息listPageByParentId按目录 ID 列文件sync加download按文件 ID 取内容有个容易漏的细节请求要带一套完整的浏览器头User-Agent 等脚本照抄了一遍否则会被拒之门外。放弃账号密码登录改用 Cookies 登录方案拿账号密码接口登录的尝试死在图形验证码上。换条路直接把已登录浏览器的身份借过来——从 DevTools 复制三个 Cookie 写进cookies.json脚本完全不做登录动作只把它们塞进会话。这也是项目反复提醒别把这个文件推到公开仓库的原因它等价于你的登录态。# cstk 取自 Cookie拼进每个请求的 query target list_url.format(folderfolder_id, tokenself.cstk) return self.session.get(target).json()三个差点让项目下马的坑 坑一图片挂在围墙里现象MD 打开一切正常图片全是灰叉。链接全指向note.youdao.com这个图床只在有道云笔记体系内生效登录态一变链接即废。一次失败的尝试保留远端链接赌浏览器能打开MD 阅读器总该能吧。Obsidian、Typora 统统加载不出来图片和登录会话是绑死的。最终解法转换完 MD 后用正则扫出note.youdao.com的图片与附件链接下载进同目录的images、attachments文件夹再改写路径若在 config.json 里填了smms_secret_token就改传 SM.MS 图床上传失败自动回落本地。逻辑集中在 core/image.py。坑二表格转换为什么最难现象表格输出一行挤或者表头错位整张表塌陷成乱码。难点在于 XML 里表格的 content 是一层二次编码的 JSON 字符串单元格是平铺数组得按widths列宽自己切成行。Markdown 表格还挑剔必须插一行| --- |分隔行只有一行的表格没有表头得凭空补一行空白标题。单元格内的字符是雷区*、_、#必须转义换行只能变br。一次失败的尝试直接把单元格文本用|串起来单元格自己带竖线或换行的时候结构当场崩掉。最终解法先解析成二维数组补分隔行逐格转义再落笔# 表格内容是一层 JSON 字符串得按列宽切回行 for cell in json.loads(content)[cells]: row.append(cell.get(value) or ) if len(row) width: lines.append(row) row []代码块、高亮块同理要保住语言属性用围栏包起来换行统一用\r\n。坑三换行符与路径的跨平台差异现象macOS 上顺风顺水Windows 上一更新文件就抛WinError 183覆盖写入失败。一次失败的尝试直接f.write()覆写再用os.utime统一同步文件时间——Windows 上创建时间与修改时间的行为不同时间根本设不进去。最终解法三处特判。覆盖前先删旧文件同步时间时 Windows 走win32_setctime、其他平台走os.utime把云端修改时间写进本地文件增量同步第二次判断才准路径统一把\换成/换行符统一\r\n。拿到工具之后你能把它拧成什么样 增量同步只下载变化的笔记每次运行都是全量比对但代价很低接口给每篇笔记返回修改时间脚本拿它和本地文件的修改时间比云端不比本地新就跳过本地已经改过的文件不会被覆盖。唯一的禁忌是云端和本地同时改同一篇本地修改可能丢。想只备份某个目录、或切换图片路径模式都改 config.json 里的字段即可。如果你也想改四个方向继承转换类加一种新的导出格式复用 API 层加 push 做双向同步给限流场景加重试与退避把图床换成私有对象存储看 pull.py 的调度就明白第一条为什么容易节点名即方法名新增一种元素类型等于新增一个静态转换方法不用动主流程。给同类工具开发者的三句话第一句逆向的起点不是代码是数据流。先回答数据存在哪、谁在拉接口只是数据的管道管道找对了剩下的都是体力活。第二句做格式转换先留原始字节再原地重写。转换成功才删旧文件失败直接可重试可回滚的管线比漂亮的报错值钱得多。第三句跨平台项目的真正成本不在主流程而在换行符、路径分隔符和文件时间这些边角上。想动手的话git clone https://gitcode.com/gh_mirrors/yo/youdaonote-pull之后先跑一遍 test/ 里的用例接口被 mock 掉转换逻辑真实执行这是读这个项目源码最快的入口。【免费下载链接】youdaonote-pull 一个一键导出 / 备份「有道云笔记」所有笔记的 Python 脚本。 A Python script to export/backup all the notes of the Youdao Note.项目地址: https://gitcode.com/gh_mirrors/yo/youdaonote-pull创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考