MARC .iso → CSV 转换(高性能版)
脚本 scripts/convert_marc.py 将 MARC 编目文件转为 UTF-8 BOM CSV,支持百万级记录。
何时使用
当用户请求符合以下任一模式时,执行本技能:
- "帮我把这个 .iso 文件转成 Excel"
- "这批 CIP 数据导出成表格,有几十万条"
- "MARC 文件打开看不懂,转成能读的格式"
- "convert MARC to CSV/Excel"
- 用户提供了 .iso / .mrc / .marc / .dat 文件并要求转换或查看内容
- 用户提到"大规模""批量""百万""千万"等数量词 + MARC/编目数据
何时不用
- 文件是 MARCXML(.xml)而非 ISO 2709 二进制格式 —— 本脚本不支持
- 用户只是想"查看文件内容"而非转换 —— 用
head/cat或 hexdump - 用户只需要快速看一眼前几条 —— 使用
-p N预览模式,无需完整转换
操作步骤
1. 确认依赖
先检查 pymarc 是否已安装。若未安装,执行:
pip install pymarc
2. 执行转换
完整转换(单文件):
python scripts/convert_marc.py <输入.iso> [输出.csv]
不指定输出路径时,在同目录生成同名 .csv。
预览模式(不写文件,仅打印前 N 条):
python scripts/convert_marc.py <输入.iso> -p 10
批量转换目录下所有 .iso 文件:
for f in /path/to/*.iso; do python scripts/convert_marc.py "$f"; done
3. 观察进度
脚本分两阶段执行,每阶段显示实时进度:
📖 data.iso (950.3 MB)
Phase 1/2: Scanning records + discovering columns...
→ 900,000 records, 156 columns (12.3s, 73,171 rec/s)
Phase 2/2: Writing CSV...
[█████████████▌ ] 45.2% 406,800/900,000 | 8,234 rec/s | ETA 1m0s
→ data.csv (452.1 MB)
⏱ Total: 127.5s | Overall: 7,058 rec/s
- Phase 1: 扫描全部记录,统计总数 + 发现所有列名(不写盘,极快)
- Phase 2: 流式写 CSV,逐条读出 → 转换 → 写入,O(1) 内存
- 损坏记录自动跳过,Phase 2 结束报告跳过数
4. 验证输出
脚本执行后会打印:
- 解析到的记录总数、列数、扫描耗时
- 写入耗时、总体吞吐率
- 前 3 条记录预览(ISBN / 正题名 / 第一责任者 / 出版者 / 出版日期 / 价格 / 中图分类号)
检查预览行确认中文正常显示。若出现乱码,说明源文件编码非 GBK,需检查源文件实际编码。
输出格式
- 格式:UTF-8 with BOM CSV(Excel/WPS 直接打开,中文不乱码)
- 列顺序:按 MARC 字段号 001 → 8XX 升序排列,控制字段前置
- 列名:已映射字段使用中文(如"正题名""ISBN""出版者"),未映射字段保留原始
NNN$x标识符 - 多值处理:同一字段多个子字段值以中文分号";"拼接
- 完整字段映射参考:
references/cip_fields.md
性能参考
| 数据规模 | 文件大小 | Phase 1 | Phase 2 | 总计 | 吞吐率 | |---------|---------|---------|---------|------|--------| | 200 条 | 0.2 MB | <0.1s | <0.1s | ~0.1s | ~3,000 r/s | | 100,000 条 | ~100 MB | ~2s | ~15s | ~17s | ~6,000 r/s | | 900,000 条 | ~950 MB | ~12s | ~110s | ~125s | ~7,000 r/s |
以上为参考估算(机械硬盘),SSD 更快。吞吐率随文件增大而提升(分摊 OS 开销)。
示例
输入文件 数据.iso(200 条 CNMARC CIP 记录,GBK 编码):
$ python scripts/convert_marc.py 数据.iso
📖 数据.iso (0.2 MB)
Phase 1/2: Scanning records + discovering columns...
→ 200 records, 90 columns (0.0s, 9,031 rec/s)
Phase 2/2: Writing CSV...
[███████████████████████████████████] 100.0% 200/200 | 4,840 rec/s | ETA 0m0s
✅ 200 records (0.0s, 4,831 rec/s)
→ 数据.csv (0.2 MB)
⏱ Total: 0.1s | Overall: 3,114 rec/s
#1: 978-7-5306-9262-2 | 茶话会 | 周洁茹著 | 百花文艺出版社 | 2026 | CNY58.00 | I267
#2: 978-7-5306-9251-6 | 辰阳寓言故事集 | 傅胜必著 | 百花文艺出版社 | 2026 | CNY78.00 | I277.4
#3: 978-7-5306-9268-4 | 孤芳 | 王玉珏著 | 百花文艺出版社 | 2026 | CNY45.00 | I247.57
预览模式:
$ python scripts/convert_marc.py 数据.iso -p 5
📖 数据.iso (0.2 MB)
Phase 1/2: Scanning records + discovering columns...
→ 5 records, 54 columns (0.0s, 2,576 rec/s)
Preview (first 5):
#1: 978-7-5306-9262-2 | 茶话会 | 周洁茹著 | 百花文艺出版社 | 2026 | CNY58.00 | I267
#2: 978-7-5306-9251-6 | 辰阳寓言故事集 | 傅胜必著 | 百花文艺出版社 | 2026 | CNY78.00 | I277.4
...
故障处理
中文乱码:脚本按 GBK → UTF-8 → GB18030 → Latin-1 顺序自动探测编码。若仍乱码,编辑脚本中 decode_bytes() 函数添加正确的编码到探测列表。
解析记录数为 0:文件可能不是标准 ISO 2709 格式。用以下命令检查文件头部:
head -c 24 文件.iso | xxd # 前 5 字节应为记录长度(如 3030373836 = ASCII "00786")
pymarc 报 TruncatedRecord:不要对记录字节做 rstrip(b'\x1d\x1e'),脚本已处理此问题。手动操作时务必以原始字节传入 Record()。
内存不足(超大文件):脚本自动检测文件大小,>1GB 启用 mmap 零拷贝模式(OS 按需分页,不会全量加载)。如仍不足,分拆源文件后分批转换。
转换中断:脚本无断点续传。中断后重新运行即可(输出文件会被覆盖)。对于超大文件建议先用 -p N 预览确认数据正确性。
架构说明
stream_records() ← mmap (>1GB) 或 f.read() 全量 + leader切分
│
├─→ Phase 1: 仅采集列名 + 计数(极快,不解码内容)
│
└─→ Phase 2: convert_record() → csv.DictWriter.writerow()
逐条流式写入,O(1) 内存
核心原则:全程不累积 Record 对象。stream_records() 是 Generator,每条 Record 用完即释放。
自定义字段
编辑 scripts/convert_marc.py 中的 FIELD_MAP 字典增删映射项。未映射字段不会丢失数据——列名保留为原始 MARC 标识符(如 300$b)。
Scan to join WeChat group