📚 本文是「Obsidian 知识管理」系列第一篇。同系列:

印象笔记太臃肿了——这是我决定迁移的原始动力。70 多条笔记、91 张图片、4 个顶层目录,最终完整迁移到 Obsidian,三列宽表能正常显示,图片引用零断裂。这篇文章把完整流程和经验教训都记录下来。

一、为什么要迁移

用印象笔记(中国版)多年,几个问题越来越难以忍受:

  • 客户端臃肿:启动慢、编辑卡顿、同步频繁失败
  • 格式锁定:笔记内容绑定在私有格式里,无法用外部工具处理
  • 迁移困难:新版 v10+ 甚至取消了标准 .enex 格式导出
  • 不可编程:无法批量处理、格式化、自动化

选择 Obsidian 的理由很简单:纯 Markdown 文件,本地存储,用 Git 管理版本,完全可控。


二、导出 ENEX:第一个坑

2.1 国内版 v10+ 无法导出 .enex

打开印象笔记 → 文件 → 导出 → 只有 .notes 格式可选,没有 .enex。

这个 .notes 是 AES 加密的私有格式,无法用任何第三方工具解析。

2.2 解决方案:安装旧版 Evernote v6.7.6

旧版国际版 Evernote 仍然支持 .enex 导出,而且能直接读取国内版的数据目录。

操作步骤:

  1. 下载 Evernote 6.7.6 安装包
  2. 安装后登录同一账号,笔记自动同步
  3. 全选笔记 → 文件 → 导出 → 格式选 ENEX
  4. 得到一个 Evernote.enex 文件(约几十 MB)

经验教训:迁移前先确认源工具是否支持标准格式导出。印象笔记的私有格式是最大的迁移障碍,好在这个坑有解——旧版客户端就是钥匙。


三、ENEX 转 Markdown:Python 脚本批量转换

.enex 本质是 XML 文件,每条笔记包含 <title>(标题)和 <content>(HTML 正文)。核心工作是解析 HTML → 生成 Markdown。

3.1 HTML 到 Markdown 的映射

印象笔记 HTMLMarkdown
<h1> ~ <h6># ~ ######
<b>/<strong>**文本**
<a href="...">[文本](URL)
<table>/<tr>/<td>Markdown 表格
<en-media hash="...">![[图片名.png]]
<en-todo checked="true">- [x] 任务
<div>段落
<br>换行

3.2 表格解析:最棘手的问题

印象笔记的表格是标准 <table> HTML,但很多笔记里表格被用于"布局"(比如多列信息),转换为 Markdown 表格时容易丢失结构。

核心挑战:

  • 单元格内容路由:<td> 内的 <a>、<b>、<br> 等标签需要正确解析和内联
  • 空单元格:HTML 的空 <td> 转换为 Markdown 时不能丢失列
  • 粗体嵌套:<span style="font-weight:700"> 不能简单当作 <b> 处理

最终方案是用 Python HTMLParser 子类,通过 _in_cell 状态标志统一管理单元格内容写入,配合 _append() 方法实现正确路由。

3.3 图片引用映射

ENEX 中图片以 <en-media hash="..." type="image/png" /> 形式引用,实际图片文件在同一目录下以 hash 命名。转换时:

  1. 从 ENEX 提取 <en-media> 的 hash 值
  2. 匹配对应的图片文件
  3. 将图片改为语义化命名(如 某楼盘-装修想法_4.png)
  4. Markdown 中用 Obsidian wiki-link 引用:![[图片名.png]]

四、自动分类:关键词 + 权威源校准

4.1 顶层目录结构

按照原来的印象笔记本结构,设计了 4 个顶层目录:

个人笔记/     → 出行旅游、房屋装修、育儿笔记、个人理财、办事指南、通用
工作笔记/     → HMD、TCL、其他客户、人力资源、认证、需求整理、通用
学习笔记/     → AI、CustomerManager、通用
通讯录/       → 扁平结构

4.2 关键词自动分类

第一轮用 Python 脚本根据笔记标题/内容的关键词自动分配到对应子目录。但初次分类准确率只有约 70%,原因是:

  • 笔记标题可能不包含明确类别词
  • 同一篇笔记可能涉及多个主题

4.3 权威源校准

后来在 <你的本地导出目录> 下发现了按笔记本分类导出的独立 ENEX 文件:

个人笔记.enex    → 27 条笔记
工作笔记.enex    → 27 条
学习笔记.enex    → 13 条
通讯录.enex      → 7 条

这些文件的顶层分类是权威的——因为它们本身就是按笔记本导出的。用这些文件做了第二轮校准,分类准确率提升到 100%。

经验教训:自动分类很美好,但最终要有一个权威源做校准。如果你的印象笔记还保留着原始目录结构,在导出时按笔记本分别导出,可以省去大量事后校准的时间。


五、图片处理:语义命名 + 去重 + 集中管理

5.1 集中存放

所有图片统一放在 Vault 根目录下的 attachments/ 文件夹。Obsidian 设置中配置 attachmentFolderPath: "attachments",全局搜索匹配。

5.2 语义化命名

原始图片文件以 hash 命名(如 f3a1b2c3.png),完全不可读。转换为:

某楼盘-装修想法_4.png  ← 包含文档名 + 序号
北京全家游-行程表_1.png

好处:去重时一眼能看出归属,删除废弃文档时能顺手清理对应图片。

5.3 图片去重

同一张图可能在多条笔记中引用。用 Python 脚本按 hash 去重,确保 attachments/ 里每张图只存一份。

5.4 修复引用断裂

转换过程中出现过"所有图片引用都指向同一张图"的 bug。原因是重构脚本时错误地覆盖了图片映射表。修复方法:重新从原始 ENEX 提取每个文件的图片引用关系。

经验教训:图片引用是迁移中最容易出错的环节。每轮格式修改后都做一次全量图片引用校验(![[ 匹配 + 文件存在性检查),避免累积错误。


六、Markdown 格式化与清理

6.1 标题层级修复

ENEX 转换后的 Markdown 可能有:

  • 多余的 # 前缀叠加(如 ## ## 标题)
  • 标题级别不统一

通过正则清理,确保每个 # 标题干净、层级合理。

6.2 空行压缩

转换后部分文件有大量连续空行(最多的 15 行连续空白)。用脚本把连续空行压缩到最多 2 行,同时保护代码块区域不被误处理。

6.3 表格重建

初始转换后,14 个文件的表格出现碎片化——单元格内容跑到表格外面去了。原因是 HTML 解析时 _pending_text 和 _current_cell 路由错误。

修复方法:

  1. 引入 _in_cell 布尔标志
  2. 所有文本写入通过 _append() 统一路由
  3. </span> 不触发 ** 闭合

修复后从 ENEX 源文件直接全量重建,零残留碎片。


七、表格显示优化:CSS 片段

Obsidian 默认表格不会换行也不会滚动。对于多列表格(如 9 列的「北京全家游」),内容被推到屏幕外。

七.1 两套方案并行

方案适用场景做法
A: 横向滚动>= 5 列overflow-x: auto + white-space: nowrap
B: 单元格换行<= 4 列word-wrap: break-word + white-space: normal

七.2 实现方式

创建 table-wrap.css 放入 .obsidian/snippets/:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* 方案 B:默认所有表格单元格换行 */
.markdown-reading-view table td,
.markdown-reading-view table th {
    white-space: normal;
    word-wrap: break-word;
}

/* 方案 A:带 cssclasses 标记的文件启用横向滚动 */
.table-scroll .markdown-reading-view .table-wrapper {
    overflow-x: auto;
}

然后用 Obsidian 的 cssclasses frontmatter 功能,给 5 个多列表格文件加上 cssclasses: [table-scroll],精确控制哪些文件用滚动方案。

自动判断逻辑:扫描所有文件 → 统计最大列数 → >= 5 列加 table-scroll,其余默认换行。

经验教训:不要一刀切。3 列表格换行很自然,9 列表格换行反而更乱。用 cssclasses 做文件级别的精细控制,比全局 CSS 优雅得多。


八、MOC 导航文件

为每个顶层目录创建了 Map of Content(MOC)文件,作为导航入口:

README.md          → 仪表盘,链接到 4 个顶层目录 MOC
个人笔记 MOC.md
工作笔记 MOC.md
学习笔记 MOC.md
通讯录 MOC.md

每个 MOC 文件列出该目录下所有子目录和笔记链接。


九、最终成果

指标数值
笔记总数74 篇 (.md)
图片总数91 张
顶层目录4 个
子目录18 个
图片引用断裂0
孤儿图片0
表格碎片文件0(全部重建)
含 cssclasses 文件5 个

十、经验教训总结

  1. 导出前确认格式:国内版印象笔记 v10+ 不提供 ENEX 导出,提前下载旧版 v6.7.6
  2. 保留权威分类源:按笔记本分别导出 ENEX,比事后关键词分类准确得多
  3. 图片引用每轮校验:每做一轮格式处理就做一次全量图片引用检查
  4. 表格用原生 HTML 解析:不要用正则处理 <table>,HTMLParser 状态机是最稳妥的方案
  5. CSS 胜过改内容:表格显示问题用 CSS 片段解决,不动文档内容
  6. 多列滚动、少列换行:用 cssclasses 做文件级精细控制
  7. 增量迭代不如全量重建:出错后与其打补丁,不如从原始 ENEX 全量重建——更快更干净