采集运维手册
年度校准、增量采集与数据质量维护的操作规程。
采集运维手册(Runbook)
面向维护者。定义 post-stats.gov.cn 时代的采集节律、增量/全量两条管线的操作步骤、产物合并策略与层级冻结边界。 依据:数据采集现状评估与提升路径、采集能力提升实施计划。
1. 采集节律(对齐法定发布机制)
《行政区划代码管理办法》(民政部令第79号,2025-09-01 施行)第十六条确立:国务院民政部门每年 1 月通过国家地名信息库(dmfw)发布截至上年末的全国各级行政区划代码。据此,采集分两条节律:
| 节律 | 触发 | 管线 | 目的 |
|---|---|---|---|
| 年度全量校准 | 每年 1 月(法定发布后) | dmfw 全量 BFS → 差分基线 → patch | 与官方年度发布对齐,重建当年全量基线 |
| 日常增量 | 变更事件驱动(周/月轮询) | xzqh 变更公告 → NLP 抽取 → patch | 捕捉年内新设/撤并/更名,无需全量重爬 |
首年确认:79 号令 2025-09 才生效,2026-01 是首个完整履行周期。执行年度校准前,先人工确认 dmfw 数据现势日期(抽查任一节点,确认已更新至上年 12-31),再触发全量,避免抓到未更新的旧态。
2. 日常增量:xzqh 事件驱动(首选)
xzqh 是权威变更事件源,带明确批复机关+日期,仍优于"dmfw 全量差分反推变更"(后者为推断、无批复证据)。
注:dmfw 差分曾会混入 dmfw/NBS 层级口径差异造成的伪变更(48 直辖市区 + 5 省直管市假
move),该问题已由diffToPatch内置的canonicalizeParent归一化关闭(见 §3 与 memorycrawler-dmfw-nbs-level-mismatch);xzqh 首选是因证据等级更高,而非 dmfw 层级不可用。
# 抓某年《县级以上行政区划变更情况》→ NLP 抽取 → 产出 draft patch
pnpm --filter @cndiv/crawler crawl:xzqh --year=2026
# 或用已构建的 bin
cndiv-xzqh --year=2026- 数据源:
xzqh.mca.gov.cn/description?dcpid=<年份>(dcpid即 4 位年份,GBK 编码,无 RSS/API)。 - 覆盖:县级以上变更(设立/撤销/更名/驻地迁移/隶属调整)。1999–2026 连续;2022 年无发布(县级调整冻结期)属正常。
- 产物:合法
operations经validatePatch守门;新设实体无既有码 → 落unresolved待人工分配码后补add(不臆造码)。 - 监控建议:周级轮询 xzqh 当年页面,内容差分发现新条目即触发抽取。
3. 年度全量校准:dmfw BFS 差分
# 全国全量(每年 1 月,对齐 2023 基线)
pnpm --filter @cndiv/crawler crawl --year=2026 --baseline=packages/source-2023/data/divisions.csv
# 分省验证/分批:--root=640000000000
# 断点续爬:相同 --cache 目录重跑即续跑- 引擎:
getListJSON 接口 +maxLevel=2步长 BFS(全国全量 ~461 请求,见 T1.1)。 - 覆盖:level 1–4(省/市/县/乡镇街道),无村级。
- 层级归一化(自动):
diffToPatch内置canonicalizeParent——以 12 位码结构派生父码覆盖 dmfw 上报的扁平父码,对基线/当前两侧施加;isPlaceholder在 remove 分支豁免「市辖区 / 省直辖县级行政区划」占位层。故直辖市区/省直管市的层级建模差异不再产假move/假remove,无需人工预对齐(首跑 92 假 ops 已归零)。 - 差分默认抑制
remove(--removes=off):dmfw 覆盖 < NBS,"基线有、dmfw 无"多为口径差异而非真实撤销;需人工复核时--removes=on。 - 产物按省切分写
patches/<year>/<省码>0000000000-dmfw-<year>.json,逐个经validatePatch守门。
4. 两条管线的产物合并与去重
两条管线都产出 patches/<year>/*.json,应用顺序与去重原则:
- 事件流优先:xzqh 抽取的变更带明确批复机关+日期(权威证据),置信度高于 dmfw 全量差分的推断。
- 同码冲突:同一
code若 xzqh 与 dmfw 都有操作,以 xzqh(事件源)为准;dmfw 差分结果作交叉印证。 - 应用即校验(两道守门):
cndiv apply-patch前所有 patch 必过 ①validatePatch(字段形状,pnpm validate-patches)+ ②cndiv-verify --mode=structural(码/层级/父级自洽 + 对 baseline 引用完整性,schema 之上的语义层)。两者 CI 已接入,任一 error 阻断。详见 patch-校验与交叉校验。 - 置信度分级:
official_nbs > mca_decree > community > shadow_map(见 spike 文档)。
4.2 结构性门禁 cndiv-verify(本地/CI 通用)
# 提 patch 前本地自查(CI 同款门禁):任一 error → 退出码 1
cndiv-verify --mode=structural --patch=patches --baseline=packages/source-2023/data/divisions.csv- 离线确定性:只读 baseline 码集 + 码工具,无网络——门禁不会因 dmfw 反爬随机变红。
- 门禁红了怎么办:看规则 id 定位。
ADD_PARENT_MISMATCH/ADD_LEVEL_MISMATCH=码与声明不自洽(多为手写 patch 笔误或未归一化的外部 patch);TARGET_MISSING/ADD_DUPLICATE=引用了 baseline 不存在/已存在的码(常是虚构码或基线年份错配);*_MISSING为 warning 不阻断(多跨 patch 引用)。 - 商业地图交叉校验:
--mode=cross为合规桩,商业源三重禁止(存储/构建数据集/分发),仅限本地手动、只读、产物不落库,不接入 CI、不入 MIT 再分发。
4.1 合并工具 cndiv merge-patches(T2.3)
多份 patch 先合并去重成单一成品,再交 apply-patch(apply-patch 单文件事务不变):
cndiv merge-patches --dir=raw/2026 --out=patches/2026/2026.merged.json
# 可选覆盖优先级:--priority=xzqh,community,dmfw(高→低)- 管线优先级:
xzqh > community > dmfw。community 居中是本手册的运维判断——社区提交经人工核证、证据等级通常介于事件流与全量差分之间;如某年社区质量存疑,用--priority临时降级。 - 冲突键 =
code:同一 code 被多管线触碰时,败者该 code 上所有 op 整组落 sidecar(<out>.conflicts.json)。注意:若败者带正交操作(如 dmfw 同 code 的 move 与 xzqh 的 rename 无关),也会连带进 sidecar——冲突报告须人工过一遍,字段级合并暂不做(Occam)。 - 管线判定:优先读 patch
meta.source_pipeline(生产者cndiv-crawl盖dmfw、cndiv-xzqh盖xzqh,单一真相源);仅当缺该字段(老/手写 patch)时才回退文件名/author 启发式(含xzqh→xzqh,含dmfw→dmfw,否则 community)。故新产 patch 无需依赖文件名辨识,社区手写 patch 建议显式写meta.source_pipeline以免启发式误判。 - 其它不变量:完全相同 op 去重、
addvsremove同 code 保留add、输出恒序add→move→update→remove、apply_after不一致直接报错拒合并。
5. 层级冻结边界(T2.2)
level 5 村级(村委会/居委会,NBS 2023 基线 620,572 条)已永久冻结:
- 官方无活源:79 号令法定代码体系只到乡级(九位码:街道/镇/乡/民族乡/区公所);村/居委会是基层群众性自治组织,非行政区划建制。dmfw 接口结构性止于 level 4,高德等商业源亦止于街道。完整论证见 spike-village-level5。
- 冻结地板:NBS 2023 村级作为不可增量的冻结地板(
source_type=official_nbs);村级变更(撤并/新设社区/更名)走既有 Patch 通道(patches/),来源为各地民政局公示的 NLP 抽取,而非全量爬虫。 - 差分不变量:
diffToPatch默认levels=[1,2,3,4],对 level5 不产出任何 add/update/remove(由test/diff.test.ts固化)。仅当显式传levels=[5]时才比对——排除是默认策略而非硬编码禁止。
数据侧说明:
packages/source-*/data/*.csv按.gitignore不入库(走 NPM/Release 分发),故冻结状态以本文档 + spike 文档 + 测试断言声明,不逐行改数据。
6. 工程注意(反爬/稳定性)
- dmfw/xzqh 均为政府接口,无 SLA、路径会漂移(已实锤
/server/resource.html→/resource.html)。采集失败逐 code 记录、可续爬;接口契约建议纳入定期 smoke test(见 plan T3.2)。 - dmfw
getList有间歇性抖动(同 root 连查偶有丢子树);全量校准后应比对总条数量级(全国 ~2446 条 level1-4 或按当年)确认无系统性丢失,failures 节点重跑补齐。 - 数据不进 git:
.cache/、大 SQLite/CSV 走.gitignore内容型规则 + pre-commit 钩子(见 memorydata-asset-backup)。
7. 年度校准提醒
.github/workflows/annual-recalibration-reminder.yml 于每年 1 月中旬自动开 issue 提醒执行年度全量校准。注意:GitHub 对 60 天无提交活动的仓库会自动禁用 scheduled workflow,长期休眠后需手动重新启用或 workflow_dispatch 触发。