Papa Labs

Google Keep 笔记迁移指南:Takeout 导出转 Markdown

Keep 里的笔记要搬进 blog(或者 Obsidian),路线是固定的:Takeout 导出 → 脚本转换。全程不需要任何第三方服务碰你的数据。

Step 1:Takeout 导出

  1. 打开 takeout.google.com
  2. 先点「取消全选」,然后只勾选 Keep
  3. 导出格式选 zip,创建导出 —— 笔记不多的话几分钟邮件就到。

Google Keep 产品页

Keep 的所有内容(包括图片附件和清单勾选状态)都会进 Takeout 包

Step 2:看懂导出内容

解压后每条笔记有两个文件:.html(阅读用)和 .json(转换用)。JSON 结构大致是:

{
  "title": "FortiGate HA 心跳口配置",
  "textContent": "笔记正文……",
  "labels": [{ "name": "networking" }],
  "isTrashed": false,
  "isArchived": false,
  "userEditedTimestampUsec": 1719800000000000
}

几个要点:

  • 普通笔记正文在 textContent清单类笔记在 listContent(数组,每项有 textisChecked);
  • isTrashed: true 的是回收站里的,转换时要跳过;
  • 图片附件在 attachments,文件就在同一个文件夹里;
  • Keep 的标签(labels)可以直接映射成 blog 的 tags。

Step 3:批量转换脚本

核心逻辑就几十行 Node(放在 scripts/keep2md.mjs 之类的位置):

import fs from 'node:fs';
import path from 'node:path';

const src = './Takeout/Keep';
const out = './inbox';

for (const f of fs.readdirSync(src).filter(f => f.endsWith('.json'))) {
  const note = JSON.parse(fs.readFileSync(path.join(src, f), 'utf8'));
  if (note.isTrashed) continue;

  const date = new Date(note.userEditedTimestampUsec / 1000)
    .toISOString().slice(0, 10);
  const tags = (note.labels ?? []).map(l => l.name);
  const body = note.textContent
    ?? (note.listContent ?? [])
      .map(i => `- [${i.isChecked ? 'x' : ' '}] ${i.text}`)
      .join('\n');

  const md = `---\ntitle: ${note.title || '(untitled)'}\ndate: ${date}\ntags: [${tags.join(', ')}]\ndraft: true\n---\n\n${body}\n`;
  fs.writeFileSync(path.join(out, f.replace('.json', '.md')), md);
}

所有产出先进 inbox/ 并且默认 draft: true —— 转换是批量的,筛选是人工的。值得发布的挪进 src/content/posts/,其余留着当私人档案。

教训预告

Keep 的笔记普遍是「一句话备忘」,真正能成文的可能只有两三成。别指望全自动,转换只是把原料备好。

← 全部文章