📚 本文是「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 导出,而且能直接读取国内版的数据目录。
操作步骤:
- 下载 Evernote 6.7.6 安装包
- 安装后登录同一账号,笔记自动同步
- 全选笔记 → 文件 → 导出 → 格式选 ENEX
- 得到一个
Evernote.enex文件(约几十 MB)
经验教训:迁移前先确认源工具是否支持标准格式导出。印象笔记的私有格式是最大的迁移障碍,好在这个坑有解——旧版客户端就是钥匙。
三、ENEX 转 Markdown:Python 脚本批量转换
.enex 本质是 XML 文件,每条笔记包含 <title>(标题)和 <content>(HTML 正文)。核心工作是解析 HTML → 生成 Markdown。
3.1 HTML 到 Markdown 的映射
| 印象笔记 HTML | Markdown |
|---|---|
<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 命名。转换时:
- 从 ENEX 提取
<en-media>的 hash 值 - 匹配对应的图片文件
- 将图片改为语义化命名(如
某楼盘-装修想法_4.png) - 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 路由错误。
修复方法:
- 引入
_in_cell布尔标志 - 所有文本写入通过
_append()统一路由 </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/:
| |
然后用 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 个 |
十、经验教训总结
- 导出前确认格式:国内版印象笔记 v10+ 不提供 ENEX 导出,提前下载旧版 v6.7.6
- 保留权威分类源:按笔记本分别导出 ENEX,比事后关键词分类准确得多
- 图片引用每轮校验:每做一轮格式处理就做一次全量图片引用检查
- 表格用原生 HTML 解析:不要用正则处理
<table>,HTMLParser状态机是最稳妥的方案 - CSS 胜过改内容:表格显示问题用 CSS 片段解决,不动文档内容
- 多列滚动、少列换行:用
cssclasses做文件级精细控制 - 增量迭代不如全量重建:出错后与其打补丁,不如从原始 ENEX 全量重建——更快更干净