Karakeep 书签导入完全指南:从 Chrome、Firefox、Pocket、Omnivore 迁移到自托管书签库
【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder
本文围绕 Karakeep(自托管 bookmark-everything 应用)的「书签导入」能力展开,系统讲解 Karakeep 支持的导入格式(Netscape HTML、Pocket CSV、Omnivore JSON 等)、Chrome/Firefox/Pocket/Omnivore 四大主流来源的导出到导入完整链路,以及面向技术用户的 CLI 批量导入方案。读完本文,你将掌握如何把既有书签库无损迁移进 Karakeep——标题、标签、收藏日期均会被保留,所有书签自动归入一个新建的列表,并自动触发抓取与 AI 打标流程。
导入能力总览
Karakeep 的导入功能位于「设置」页面,入口对应两个按钮:「Import Bookmarks from HTML file」与「Import Bookmarks from Pocket export」。它支持三类官方文档承诺的格式:
| 格式 | 来源 | 文件类型 | 保留字段 |
|---|---|---|---|
| Netscape HTML Format | Chrome、Firefox 导出 | .html | 标题、标签、添加日期、文件夹层级 |
| Pocket CSV | Pocket 新导出格式 | .csv | 标题、标签、添加日期、归档状态 |
| Omnivore JSON | Omnivore 导出数据 | .json(可合并) | 标题、标签、保存时间、归档状态 |
导入时,标题(title)、标签(tags)与添加日期(addition date)会被完整保留,并且系统会自动创建一个新列表(list)收纳本次导入的全部书签,方便你事后统一管理与迁移核对。
一个必须提前知晓的约束(官方文档以 info 提示框特别强调):
书签文件中的所有 URL 都会被自动添加,你无法在导入时挑选哪些书签要导入、哪些不要。
因此建议在导出前先自行清理目标书签库,避免把不需要的链接一并灌入 Karakeep。
从源码角度看,导入格式的实际支持面比文档承诺的更广。在 packages/shared/import-export/parsers.ts 中,ImportSource类型枚举了全部受支持的导入源:
export type ImportSource = | "html" // Netscape HTML(Chrome / Firefox) | "pocket" // Pocket CSV | "matter" // Matter CSV | "omnivore" // Omnivore JSON | "karakeep" // Karakeep 自身导出 JSON | "linkwarden" // Linkwarden JSON | "tab-session-manager" // 浏览器标签会话 JSON | "mymind" // mymind CSV | "readwise-reader" // Readwise Reader CSV | "instapaper" // Instapaper CSV | "onetab"; // OneTab 纯文本每种来源在parsers.ts中都对应一个独立的解析函数(如parseNetscapeBookmarkFile、parsePocketBookmarkFile、parseOmnivoreBookmarkFile),并由统一的入口parseImportFile(source, textContent)分发调用。这些解析器均使用 zod schema 校验输入结构,格式不符会抛出明确的错误信息(例如上传的 HTML 缺少<!DOCTYPE NETSCAPE-Bookmark-file-1>声明时会提示 "The uploaded html file does not seem to be a bookmark file")。不同来源的标签分隔符差异也在解析层被归一化:Netscape HTML 用逗号分隔、Pocket 用|分隔、Matter 用;分隔、mymind 用逗号分隔、Readwise Reader 的标签是 JSON 数组字符串,最终都会统一转换成 Karakeep 的标签数组。
从 Chrome 导入书签
Chrome 是存量书签最多的浏览器,Karakeep 通过 Netscape HTML 格式与 Chrome 互通。完整操作步骤:
- 打开 Chrome,在地址栏输入
chrome://bookmarks进入书签管理器; - 点击右上角的三个点(⋮)菜单,选择Export bookmarks(导出书签);
- 浏览器会下载一个包含全部书签的 HTML 文件(文件名形如
bookmarks_YYYY-MM-DD.html); - 打开 Karakeep 的「设置」页面,点击Import Bookmarks from HTML file,选择刚下载的 HTML 文件即可。
导入后,Chrome 书签栏中的文件夹层级会被保留:parseNetscapeBookmarkFile(packages/shared/import-export/parsers.ts)通过 cheerio 递归遍历书签文件中的DT/H3(文件夹)与A(书签)节点,把文件夹路径记录在paths字段中;同时读取add_date属性作为添加日期、读取tags属性作为标签。Karakeep 会在后续流程中依据这些路径重建列表结构。
从 Firefox 导入书签
Firefox 同样导出 Netscape HTML 格式,操作路径稍有不同:
- 打开 Firefox,点击右上角的菜单按钮(☰);
- 进入书签 > 管理书签(或直接按快捷键
Ctrl + Shift + O/Cmd + Shift + O打开书签库窗口); - 在书签库顶部点击导入和备份(Import and Backup)按钮,选择将书签导出为 HTML...(Export Bookmarks to HTML...),把全部书签保存为 HTML 文件;
- 回到「导入和备份」菜单,选择从 HTML 导入书签...(Import Bookmarks from HTML...),选中你保存的 HTML 文件即可完成导入。
Firefox 导出的文件同样以<!DOCTYPE NETSCAPE-Bookmark-file-1>开头,与 Chrome 文件在 Karakeep 解析层走的是同一条parseNetscapeBookmarkFile代码路径,因此标题、标签、添加日期与文件夹层级的行为完全一致。
从 Pocket 导入书签
Pocket 的导出采用其新版 CSV 格式,流程如下:
- 访问 Pocket 的导出页面(getpocket.com/export),按页面指引发起导出;
- 几分钟后 Pocket 会把包含全部书签的 zip 压缩包发送到你的邮箱;
- 解压 zip 得到 CSV 文件;
- 打开 Karakeep「设置」页面,点击Import Bookmarks from Pocket export,选择该 CSV 文件。
在解析层,parsePocketBookmarkFile(packages/shared/import-export/parsers.ts)使用csv-parse/sync按列读取记录:title映射标题、url映射链接、time_added(Unix 秒级时间戳)映射添加日期、tags以|分隔拆成标签数组、status为archive的记录会标记为已归档(archived: true)。这意味着 Pocket 中「已归档」的书签导入 Karakeep 后也会保持归档状态,不会混入你的活跃书签流。
从 Omnivore 导入书签
Omnivore 的导出是一个包含全部数据的 zip 包,其中书签数据以多个metadata_*.json文件形式存在。导入方式有两种:
- 逐个导入:手动把每个
metadata_*.json文件分别上传导入(适合文件数量少的情况); - 合并后导入:把多个 JSON 合并成单个
omnivore.json再导入(推荐,一次完成)。
合并命令(需要在解压目录下执行,且系统需安装 [jq] 工具,即 jqlang/jq):
jq -r '.[]' metadata_*.json | jq -s > omnivore.json命令含义拆解:jq -r '.[]'把每个metadata_*.json中的数组元素展开为 JSON Lines 流;jq -s(slurp)再把流中的全部对象收集成一个 JSON 数组,最终写入omnivore.json。之后在 Karakeep 设置中选择导入该文件。
从解析实现看,parseOmnivoreBookmarkFile(packages/shared/import-export/parsers.ts)期望一个 JSON 数组,每个元素包含title、url、labels(标签数组)、savedAt(保存时间)与可选的state字段;state为Archived的书签会被标记为归档。这也解释了为什么合并命令必须产出「对象数组」——单个metadata_*.json本身已经是数组,多个文件若不合并就无法一次解析。
使用 CLI 批量导入书签
当书签数量庞大、或来源格式不在上述标准格式之列时,可以使用 Karakeep CLI 逐条导入。官方文档明确提示:该方式需要一定技术基础,对非技术用户可能不够直观;遇到问题可在 GitHub Discussions 或 Discord 中提问。
前提条件:你手头有一份每行一个链接的纯文本文件(例如all_links.txt),且已安装并配置好 karakeep CLI(配置方式见 命令行工具文档,需要--api-key与--server-addr两个全局参数)。
批量导入命令(Linux/macOS shell):
while IFS= read -r url; do karakeep --api-key "<KEY>" --server-addr "<SERVER_ADDR>" bookmarks add --link "$url" done < all_links.txt命令逻辑:while IFS= read -r url逐行读取all_links.txt并把每行内容赋给变量url(IFS=保证行首行尾空格不被吞掉,-r防止反斜杠被转义);循环体内调用karakeep ... bookmarks add --link "$url"逐条创建书签。每条 URL 调用一次 CLI 进程,适合中等规模迁移;若 URL 量极大,可自行加上并发或改用导入 API。
从源码看,CLI 的bookmarks add子命令定义在 apps/cli/src/commands/bookmarks.ts 中,它通过 tRPC 客户端调用bookmarks.createBookmarkmutation 完成创建;该 mutation 自带重复检测能力(URL 已存在时返回alreadyExists标记),因此即便文本文件里有重复链接,也不会在 Karakeep 中生成重复书签。CLI 也支持--json等全局输出选项便于脚本化处理。
导入背后的工作原理
理解导入的内部机制,有助于预判导入耗时、排查失败项。Karakeep 的导入并非一次性同步写入,而是「暂存(staging)→ 后台异步处理」的架构。
前端到服务端的调用链
Web 端的导入流程封装在 apps/web/lib/hooks/useBookmarkImport.ts 与 apps/web/lib/hooks/useImportSessions.ts 中,对应的服务端路由位于 packages/trpc/routers/importSessions.ts。核心 tRPC 接口包括:
createImportSession:创建导入会话(含会话名称与根列表 id);stageImportedBookmarks:把解析出的书签分批(单次最多 50 条)写入暂存表importStagingBookmarks;finalizeImportStaging:暂存完成后将会话置为待处理(pending),通知后台 worker 开工;pauseImportSession/resumeImportSession:暂停/恢复导入,可在后台抓取压力大时手动控制;getImportSessionResults:分页查询每个暂存书签的处理结果(accepted/rejected/skipped_duplicate/pending)。
底层编排逻辑见 packages/shared/import-export/importer.ts 的importBookmarksFromFile:它会先用createList创建一个以「⬆️」为图标的根列表(名称由调用方指定),再创建导入会话,把解析出的书签批量 stage 进去,最后finalizeImportStaging触发后台处理。若导入文件是 Karakeep 自身的导出 JSON,还会顺带重建其内部列表层级(importer.ts中的externalListIdToCreatedListId映射负责把源文件中的列表 id 关联到新创建的列表)。
后台 ImportWorker 的处理状态机
真正的书签落库由独立 worker 完成,实现位于 apps/workers/workers/importWorker.ts 的ImportWorker类。它以 5 秒为轮询间隔(pollIntervalMs = 5000)扫描暂存表,核心机制包括:
- 分批认领(claim):每批最多 10 条(
batchSize),通过原子 UPDATE 把pending状态置为processing,避免多个 worker 实例重复处理同一条记录; - 背压控制(backpressure):同时在途处理上限为 50 条(
maxInFlight),达到上限后暂停认领,防止瞬时创建过多书签压垮抓取队列; - 用户间公平调度:
getNextBatchFairly按「用户最近处理时间 + 暂存创建时间」排序取批,保证多用户实例中每个用户的导入都能推进; - 下游联动:书签创建后暂存项保持
processing,直到其**抓取(crawl)**与AI 打标(tagging)都完成(checkAndCompleteProcessingItems检查crawlStatus与taggingStatus),才标记为completed——这就是导入后书签会陆续出现标题、标签的原因; - 失败与重试:抓取或打标失败的书签标记为
failed(原因分别为 "Crawl failed" / "Tagging failed");超过 1 小时仍卡在processing且未创建书签的僵尸项会被重置回pending重新尝试;重复 URL 直接记为skipped_duplicate,不会重复入库; - 会话收尾:一个会话下所有暂存项处理完毕后,会话状态置为
completed并记录事件日志(含导入来源与成功数量),超过 30 天的已完成会话会被归档清理。
这些行为意味着:导入大文件后无需守在页面上,Karakeep 会在后台持续消化队列;最终可在导入会话详情页中按accepted、rejected、skipped_duplicate等筛选条件查看每一条的最终结果。
常见问题与注意事项
- 无法挑选导入的书签:这是设计约束,导入即全量写入;请先在原工具中清理再导出。
- 文件格式报错:上传 HTML 时提示 "does not seem to be a bookmark file",通常是文件不是 Netscape 格式(例如是浏览器「另存为网页」产物);Omnivore 提示 "invalid omnivore bookmark file" 时,检查 JSON 是否为对象数组(可用上述 jq 合并命令修复)。
- 重复书签:URL 已在库中时,导入会跳过并标记为
skipped_duplicate,不会产生重复记录,且已有书签的标签会被合并补充。 - 标签与日期保留:只有源文件确实携带对应字段时才会保留(例如 Chrome 导出的标签依赖书签原本有标签属性);标签分隔符差异由解析器自动归一化,无需手动处理。
- 归档状态:Pocket 的
archive、Omnivore 的Archived、Instapaper 的Archive文件夹等来源的归档状态会被映射为 Karakeep 的归档标记,导入后不会出现在活跃列表。 - 导入速度:取决于后台 worker 的抓取与 AI 打标吞吐,可通过导入会话页面的分页结果持续观察进度。
本文对应的官方文档见 docs/versioned_docs/version-v0.28.0/10-import.md(当前版本的镜像见 docs/docs/04-using-karakeep/import.md),导入解析器、编排逻辑与后台 worker 的完整源码分别位于 packages/shared/import-export/parsers.ts、packages/shared/import-export/importer.ts 与 apps/workers/workers/importWorker.ts,感兴趣的读者可继续深入研读。
【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考