前言

目前我的博客项目跑的 Hexo 框架,文档中的图片都是存在 Telegram,交给 Cloudflare 中转代理。但这始终有一个隐患,说不定哪天不能这么操作了,图片就全丢了。

备份是最好的容灾方案,但到现在,博客文章几十篇,图片几百张,手动备份非常麻烦。

所以这次就开发了个批量备份的工具,把图床图片都下载到本地,然后把 md 文档一起备份过去,并把内联的图床地址改成相对地址,指向备份下来的图片。这样也相当于提供了纯本地、离线的可阅读文档。

总结起来,需求便是:

  1. 把 Hexo 项目的 source 目录拆成「一篇文章一个文件夹」,引用的图片和附件一起收进该文件夹
  2. 正文里的引用改写成相对路径
  3. 备份结果不依赖 Hexo,用 Typora / Obsidian / VS Code 直接打开就能读

用法

点击下载

  1. 双击 HexoBackup.exe
  2. Hexo 项目 选到含 _config.yml 的那一层(比如 D:\magiku\magiku.hexo)
  3. 备份到 选一个目录,默认会填成项目旁边的 博客备份_年月日_时分
  4. 点 开始备份

选好项目后可以先点 扫描预览,只看统计不下载,确认认到的文章数和图片数对不对。

输出的备份目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
备份目录/
├── _posts/
│ └── A002_网络运维工具与常见故障排查/
│ ├── A002_网络运维工具与常见故障排查.md
│ └── assets/
│ ├── 1792086663731_43b11ab78fad3a2f9e812.png
│ └── ...
├── _pages/
│ └── about/
│ ├── about.md
│ └── assets/
├── _meta/
│ ├── _config.yml
│ ├── _config.butterfly.yml
│ └── package.json
├── _backup-report.md ← 失败清单,先看这个
└── _backup-report.json

正文里的 ![](https://图床/xxx.png) 会变成 ![](assets/xxx.png)。
<img src="...">、{% asset_img %}、普通链接指向的 pdf/zip 也一并处理。

几点说明

图床上的图片下载过的会缓存到 %LOCALAPPDATA%\HexoBackup\asset-cache,
30 天内重跑可直接复用,只补新的和上次失败的(增量备份)。

输出统一为UTF-8,原文件如果是 GBK,会自动识别并转换,不留乱码。

拿不到的图片保留原始 URL 原样不动,同时列进 _backup-report.md,重跑一次通常能补齐。

程序读 http_proxy / https_proxy 环境变量,即会走系统代理。

勾上「front-matter 转成 Obsidian 风格」后:

  • categories 折进 tags,去重保序
  • 日期从 2024-08-22 10:55:33 改成 ISO 8601 的 2024-08-22T10:55:33(Obsidian 的日期属性认这个格式,Dataview 才能按时间排序)
  • swiper_index、note、comments、type 这类主题和站点控制字段删掉
  • 有值的 cover / banner 保留,值已经改成本地路径;空值字段直接删

能处理哪些 Hexo 项目

只要是本地有源码的 Hexo 项目(_config.yml + source/ 那层),基本都能用。
下面这些差异都验证过:

情况 支持 说明
图片放 source/images/,正文写 /images/x.png ✅ 站点绝对路径
正文写 ../images/x.png ✅ 相对路径
图片和文章同目录散放 ✅ _posts/x.png
post_asset_folder: true ✅ 图片在 _posts/<文章名>/ 下,正文只写文件名
{% asset_img %} / {% asset_link %} ✅ 带描述参数也能认
中文文件名写成 %E4%B8%AD%E6%96%87.png ✅ 会还原后找文件
_config.yml 里自定义 source_dir ✅ 读配置,不写死 source
草稿 _drafts/ ✅ 和 _posts/ 一样当文章处理
多语言子站 source/zh-cn/_posts/ ✅ 按路径里的 _posts 判断
GBK / GB18030 编码的 md ✅ 自动识别,输出统一转 UTF-8
图片在远程图床 ✅ 下载后本地化
front-matter 里的 cover / banner / top_img / avatar 等图片字段 ✅ 字段里的图片同样下载并改相对路径,引号保留
图片在 CDN 但需要登录 / 防盗链 ⚠️ 会失败,原 URL 保留

以下情况无法备份:

  • 纯在线面板 / 托管服务(在线编辑器、Vercel、Netlify 直接连仓库构建)——本地没有项目文件夹,
    这工具需要能选到一个本地目录。如果你能从面板导出源码压缩包,解压后是标准结构,就能用。
  • 文章存在数据库里的 Hexo 变体(少数面板版),没有 .md 文件,不支持。
  • 图片地址是 permalink 形式(/2024/01/01/标题/x.png,指向 public/ 而不是 source/),
    这类路径在源码里找不到对应文件,会跳过。

源码

点击下载

源码文件解读:

文件 作用
core.py 扫描、解析引用、下载、改写、出报告。可独立命令行运行
app.py tkinter 界面
build.py 打包脚本
test_compat.py 兼容性回归测试,构造 9 种布局验证改写结果

可直接命令行启动:

1
python app.py

只依赖标准库(tkinter),Python 3.10+ 即可。

也可自行打包 exe:

1
2
python -m pip install pyinstaller
python build.py

产物在 dist/HexoBackup.exe,单文件。