Skip to content

Repository files navigation

ZIM 转 Markdown 工具

把 Kiwix 离线文库(.zim 文件,例如离线维基百科)转换成 Markdown 文章 + PNG/JPG 图片,转换结果可直接用 Typora、Obsidian 等阅读。 还能调用本地大模型(Ollama)或在线大模型,按学科把成百上千万篇文章 自动分到「理科 / 工科 / 文科 / 商科 / 医科 / 农科 / 法科 / 艺术科 / 教育科 / 军事科」 (另加「其他」兜底)下的 二级 / 三级子文件夹里,并自动跳过没有价值的消歧义页。

普通用户使用(无需编程)

  1. 双击 ZIM转MD.dmg(在 dist/ 文件夹里)
  2. 把里面的 ZIM转MD.app 拖到「应用程序」文件夹
  3. 首次打开如果提示「无法验证开发者」: 在访达里右键点 App →「打开」→ 再点「打开」(只需一次)
  4. 使用(界面有两个标签页):

「转换」标签页

  • 点「选择…」挑一个 .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 全耗在推理链上,分类速度会暴跌几十倍。

智能问答(RAG,Docker隔离)

基于 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按钮置灰并引导安装。

重新打包 DMG

修改代码后,在项目目录执行:

./build_dmg.sh

产物:dist/ZIM转MD.appdist/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 分类快(约 10400 倍)。跑整本维基(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)

About

ZIM转Markdown + 学科分类 + RAG检索(harrier/bge-reranker + Qdrant + Docker隔离,模型缺失自动拉取)

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages