把 Kiwix 离线文库(.zim 文件,例如离线维基百科)转换成
Markdown 文章 + PNG/JPG 图片,转换结果可直接用 Typora、Obsidian 等阅读。
还能调用本地大模型(Ollama)或在线大模型,按学科把成百上千万篇文章
自动分到「理科 / 工科 / 文科 / 商科 / 医科 / 农科 / 法科 / 艺术科 / 教育科 / 军事科」
(另加「其他」兜底)下的 二级 / 三级子文件夹里,并自动跳过没有价值的消歧义页。
- 双击
ZIM转MD.dmg(在dist/文件夹里) - 把里面的 ZIM转MD.app 拖到「应用程序」文件夹
- 首次打开如果提示「无法验证开发者」: 在访达里右键点 App →「打开」→ 再点「打开」(只需一次)
- 使用(界面有两个标签页):
「转换」标签页:
- 点「选择…」挑一个
.zim文件,选一个输出位置(默认桌面) - 建议先勾选「先只转换前 50 篇」看看效果,满意后取消勾选再跑全量
- 并行线程:大 ZIM 建议填 8~16,转换能快好几倍
- 跳过消歧义页(默认勾选):形如「X (disambiguation)」「X(消歧义)」、 或正文开头是 "may refer to / 可以指" 的条目不转换,省空间也省后续分类的算力
- 点「开始转换」,等进度条走完,点「打开输出文件夹」
- 如有个别文章失败,结束后点「只重跑失败」单独重试那几篇,无需整本重来
「智能分类」标签页(把转好的文章按学科分到子文件夹):
- 「待分类文件夹」选择上一步转换输出的书本文件夹(里面有 .md 和 images)
- 选模型服务:本机 Ollama,或 DeepSeek / MiniMax / OpenRouter / OpenAI / Anthropic / 智谱 BigModel / Z.ai / 阿里云百炼 / 火山方舟, 以及任意 OpenAI 兼容的自定义节点
- 在线服务需粘贴对应的 API 密钥(只保存在本机
~/.zim2md/settings.json) - 「刷新模型列表」:Ollama 列出本机已下载模型(服务没启动会自动拉起); 在线服务商实时拉取服务端最新模型清单,避免调到已下线模型。 设置里存的已停服旧模型名(如 DeepSeek 的 deepseek-chat)会自动替换为当前模型
- 可勾选「启用兜底二号位」:一号位模型失败或拒答(如敏感内容拦截)时, 自动把那些文章改交给二号位模型再试;两个都不行才记为失败。 兜底二号位同样支持「刷新模型列表」和「测试连接」
- 点「测试连接」确认能用,再点「开始分类」;失败的篇可点「只重跑失败」重试
- 分类结果按 学科 / 二级类目 / 三级类目 三层存放;单个文件夹超过 「每文件夹上限」(默认 1500 篇)会自动再按首字母分片, 即使几百万篇,Finder 打开任何一层都不会卡死
- 建议先把整本转换跑完(转换不依赖分类),之后再随时对输出文件夹做分类; 分类期间不打开输出文件夹即可,不影响转换成果
输出结构(分类前):
输出位置/书名/
├── 文章A.md ← 每篇文章一个 Markdown
├── 文章B.md
├── images/ ← 全书共用图片(自动去重)
│ ├── 3f9c2e1a0b8d7.png
│ └── ...
└── 转换报告.txt ← 成功/失败/跳过消歧义统计
分类后,文章按 学科/二级/三级 三层归入子文件夹,图片路径与文章间链接自动改写:
输出位置/书名/
├── 理科/数学/代数/勾股定理.md
├── 文科/历史/中国历史/唐朝.md
├── 商科/金融/银行/商业银行.md
├── 其他/地理/国家/法国.md
├── images/ ← 图片留在原处,文内引用按层数自动改写
├── 分类报告.txt ← 各学科分布与失败清单
└── .分类进度.db ← 进度库,支持中断续跑 / 只重跑失败
说明:
- 断点续转:中途中断没关系,再次转换同一本书会自动跳过已转好的文章
- 并行转换:转换支持多线程,libzim 并发读取已验证线程安全
- 分类可续跑:分类进度存进
.分类进度.db,中途取消/断电后再点「开始分类」会接着跑 - 只重跑失败:转换和分类两侧都有「只重跑失败」按钮,单独重试出错的篇目
- WebP 自动转 PNG;JPG/PNG/GIF/SVG 原样保存
- 文章之间的站内链接会自动改成指向对应的
.md文件(分类移动后按层数自动修正) - 有些 ZIM 本身是「无图版」(下载时选了 nopic),这种文件里没有图片可提取
# 安装依赖
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
# 图形界面
.venv/bin/python main.py
# 命令行:全部转换(8 线程并行,跳过消歧义页)
.venv/bin/python main.py --cli 某本书.zim -o 输出目录 --workers 8
# 命令行:只转前 100 篇
.venv/bin/python main.py --cli 某本书.zim -o 输出目录 --limit 100
# 命令行:连消歧义页也转出来(默认是跳过的)
.venv/bin/python main.py --cli 某本书.zim -o 输出目录 --with-disambiguation
# 命令行:只重跑上次失败的篇目(从 输出目录/书名/转换报告.txt 读失败列表)
.venv/bin/python main.py --cli 某本书.zim -o 输出目录 --retry-failed
# 命令行:智能分类(把已转换文件夹里的文章按学科分到子文件夹)
.venv/bin/python main.py --classify 输出目录/书名 --provider deepseek --api-key sk-xxx
# --provider 可选:ollama / deepseek / minimax / openrouter / openai / anthropic
# / bigmodel(智谱) / zai / bailian(阿里云百炼) / volcengine(火山方舟) / custom
# 其他可选参数:--model 模型名 --base-url 服务地址 --batch 每批篇数(默认20)
# --workers 并发数(默认4) --snippet 摘要字数(默认150,0=只用标题)
# --timeout 超时秒数 --max-per-folder 单文件夹上限(默认1500)
# --retry-failed 只重跑失败项
# 用本机 Ollama 时:--provider ollama --model huihui_ai/qwen3.5-abliterated:0.8b(无需 api-key)
#
# 一号位 + 兜底二号位(一号位失败/拒答时自动改用二号位):
.venv/bin/python main.py --classify 输出目录/书名 \
--provider minimax --model MiniMax-M3 \
--fallback-provider ollama --fallback-model "gemma4:E4b"
# 兜底参数:--fallback-provider / --fallback-api-key / --fallback-model / --fallback-base-url分类相关说明:
- 分类是「对已转换好的文章文件夹」操作的独立步骤,和转换解耦;转换崩了/中断了,
只要根目录已有部分
.md,就能先对这部分做分类。 - 采用「每批 N 篇一次请求」的方式调用大模型,百万篇文章也只需几万次 API 调用; 并发数、批大小可调。进度存 SQLite,随时可中断、续跑、只重跑失败。
- 学科固定为 10 类(理科/工科/文科/商科/医科/农科/法科/艺术科/教育科/军事科),
另设「其他」兜底地理、人物传记、体育、娱乐、饮食等不属于这 10 类的内容。
完整三层类目树见
学科分类清单.md(由zim2md/taxonomy.py生成)。 - Ollama 走原生
/api/chat接口并自动关闭思考模式(think:false)—— Qwen3 等思考模型若开着思考,会把 token 全耗在推理链上,分类速度会暴跌几十倍。
基于 Qdrant + microsoft/harrier-oss-v1-0.6b(召回)+ BAAI/bge-reranker-v2-m3(精排)+ 任意LLM生成,让大模型知道“已转换的N百万篇里有什么”。
# 1. 安装 Docker Desktop(macOS)
brew install --cask docker # 或官网 https://docs.docker.com/desktop/install/mac-install/
# 2. 一键启动向量库与RAG服务(首次自动下载模型到 ~/Models,约4.6G)
docker compose up -d
# 健康检查
.venv/bin/python main.py --rag-check
# 3. 索引已转换的文件夹(增量、断点续跑)
.venv/bin/python main.py --rag-ingest 输出目录/书名
# 持续跟进新文件(转换还在跑时)
.venv/bin/python main.py --rag-ingest 输出目录/书名 --watch
# 4. 问答(命令行)
.venv/bin/python main.py --rag-query 输出目录/书名 --q "法国的首都是什么"
.venv/bin/python main.py --rag-chat 输出目录/书名 # 交互式
# 5. 图形界面:打开 App → 第三 Tab「智能问答」→ 选文件夹 → 索引 → 提问说明:
- 模型已在
~/Models/microsoft/harrier-oss-v1-0.6b与~/Models/BAAI/bge-reranker-v2-m3就绪时直接使用;缺失则自动从 HuggingFace 拉取(支持HF_ENDPOINT=https://hf-mirror.com)。 - 仓库 不提交 模型权重(见
.gitignore),docker compose通过~/Models:/models挂载。 - 未装Docker时,转换与分类不受影响,RAG按钮置灰并引导安装。
修改代码后,在项目目录执行:
./build_dmg.sh产物:dist/ZIM转MD.app 和 dist/ZIM转MD.dmg。
| 部分 | 选择 |
|---|---|
| 语言 | Python 3.10+(在 3.14 上开发测试) |
| 读 ZIM 内容 | libzim(Kiwix 官方 C++ 库的 Python 绑定,PyPI 自带 macOS arm64/x86_64 预编译包;并发读取已验证线程安全) |
| 枚举全部条目 | zim2md/zimdir.py 自行解析 ZIM 目录区(官方 Python 绑定没有遍历 API,搜索接口空查询返回 0 条,无法枚举;目录区是未压缩的稳定二进制结构,已与 libzim C++ 解析代码逐字段核对,并在新旧两种命名空间格式的真实 ZIM 上验证一致) |
| HTML → Markdown | BeautifulSoup(改写图片/链接)+ markdownify(转换) |
| 图片格式 | Pillow:WebP/AVIF → PNG;其余原样保存 |
| 界面 | tkinter(Python 自带,转换/分类均在后台线程,界面不卡) |
| 智能分类 | 纯标准库 urllib 调用各家大模型(Ollama 原生接口 / OpenAI 兼容 / Anthropic),sqlite3 记录进度 |
| 打包 | PyInstaller → .app → hdiutil → DMG;ad-hoc 签名 |
- 转换(真实 25GB 满载中文维基,213 万条目):目录解析约 74 秒(一次性); 8 线程约 40 篇/秒(含图片提取);整本约 15 小时。小语种轻量 ZIM 可达 300+ 篇/秒。
- AI 分类:
- 本机 Ollama 0.8b(关思考):约 6~7 篇/秒,但分类质量差,大多落入「其他」兜底;
- 本机 gemma4:E4b:约 0.8~1 篇/秒,中文内容质量可用;
- MiniMax-M3 API(每批 50 篇):并发 4 约 2.4 篇/秒,并发 8 约 4.7 篇/秒, 并发 16 约 6 篇/秒,分类质量明显更好(音乐单曲、域名、化学条目都分得准), 且支持一号位失败自动转二号位兜底。
- 结论:转换远比 AI 分类快(约 10
400 倍)。跑整本维基(200 万+ 篇)时, 转换只要十几小时,而分类按 5 篇/秒算要 45 天——瓶颈在 AI 分类。 在线 API(MiniMax/DeepSeek 等)可高并发,是目前唯一实用的大规模方案。
超大 ZIM(整本中文/英文维基百科,数 GB~数十 GB)会耗时较长且占用较多内存, 建议先用「限量 50 篇」确认效果,再分时段全量转换(支持断点续转)。
# 打印单篇失败堆栈、Qdrant/RAG 详细日志
ZIM2MD_DEBUG=1 .venv/bin/python main.py --cli 某本书.zim -o 输出目录
ZIM2MD_DEBUG=1 .venv/bin/python main.py --rag-ingest 输出目录/书名针对整本维基百科这类超大文件已做内存优化:建好待转列表后立即释放完整目录索引, 避免数 GB 的条目对象常驻内存导致跑崩。
├── main.py 入口:无参数=图形界面,--cli=转换命令行,--classify=分类命令行
├── zim2md/
│ ├── zimdir.py ZIM 目录区解析(枚举条目/命名空间/重定向)
│ ├── converter.py 核心转换(HTML→MD、图片提取转格式、链接改写、续转、多线程、跳过消歧义、只重跑失败、报告)
│ ├── taxonomy.py 学科分类体系(学科/二级/三级)、提示词构造、结果解析
│ ├── llm.py 各家大模型客户端(Ollama 原生+关思考 / OpenAI 兼容 / Anthropic,纯 urllib)
│ ├── classifier.py 分类引擎(批量调用、SQLite 续跑、三层移动+字母分片、改写路径)
│ ├── settings.py 设置持久化(API 密钥等,存 ~/.zim2md/settings.json)
│ └── app.py tkinter 图形界面(转换 / 智能分类 两个标签页)
├── 学科分类清单.md 完整三层学科类目树(11 学科 / 73 二级 / 257 三级)
├── build_dmg.sh 一键打包脚本
├── requirements.txt 运行依赖
├── testdata/ 官方测试 ZIM + 端到端测试脚本(开发用)
└── dist/ 打包产物(ZIM转MD.dmg)