BYP's DB 的国保子库 national_sites.db 中,第七批以前的文物记录 Location 字段普遍缺失地级市这一层级,例如:
| Id | 名称 | Province | Location | 问题 |
|---|---|---|---|---|
| 180 | 满堂围 | 广东省 | 广东省始兴县 | 缺"韶关市" |
| 2908 | 北山摩崖造像 | 四川省 | 四川省大足县 | 缺市 + 已跨省划归重庆 |
| 2947 | 独乐寺 | 河北省 | 河北省蓟县 | 缺市 + 已跨省划归天津 |
直接后果:用户在列表页按 "韶关市" 或 "天津市" 筛选时,查不到辖下县市的文物;全文搜索 "韶关" 也只命中 Location 中字面包含 "韶关市" 的 3 条,漏掉了实际在韶关辖下的"满堂围"。
采用民政部官方公布的权威数据:
分四阶段推进,每阶段独立成脚本,便于回滚与复核:
阶段 A 基础设施:HTML → admin_division.db(3209 条扁平表)
阶段 B 诊断分析:扫描 5052 条 Location,三分类定位问题
阶段 C 增强回填:8 层规则 + 150 条历史字典 → 100% 覆盖
阶段 D 正式落库:ALTER TABLE + 批量 UPDATE + 建索引 + 前端联动
CREATE TABLE administrative_division (
code TEXT PRIMARY KEY, -- 6 位国标代码
name TEXT NOT NULL, -- 名称
parent_code TEXT, -- 父级代码(自引用)
level INTEGER NOT NULL -- 1=省 / 2=市 / 3=县
);
CREATE INDEX idx_parent ON administrative_division(parent_code);
CREATE INDEX idx_level ON administrative_division(level);
CREATE INDEX idx_name ON administrative_division(name);
clean_name() 末尾 rstrip('**') 剥离脚注星号(如 "五指山市*")parent=省级本身(北京市无地级市层)d:\BYP\Project\BYP\BYPsNotes\db\admin_division.db| 类别 | 判定 | 含义 |
|---|---|---|
| A 已含市 | Location 剥省名后以当代市名开头 | 直接可用 |
| B 命中县 | 以当代县名开头 | 通过 parent_code 回溯父级市 |
| C 未命中 | 两者都不匹配 | 需特殊处理 |
| 分类 | 数量 | 占比 |
|---|---|---|
| A | 2748 | 54.4% |
| B | 1916 | 37.9% |
| C | 388 | 7.7% |
388 条未命中清单完整存档于 诊断结果.md。
通过人工复核 388 条未命中,归纳出六种数据模式:
| 模式 | 数量 | 典型例子 |
|---|---|---|
| 1. 历史县名已撤县设市 / 改区 | ~250 | 长安县→西安市长安区、黔西县→毕节市黔西市、隆昌县→内江市隆昌市 |
| 2. 地级市改名 | ~15 | 襄樊市→襄阳市 |
| 3. 第七批"地区"未改"市" | ~37 | 昌都地区→昌都市、海东地区→海东市 |
| 4. 直辖市 + 街道/景点 | ~15 | 北京市延庆区 |
| 5. 多地并列 | ~30 | 江苏省苏州市、无锡市、常州市 |
| 6. 真正的数据错误 | ~10 | 浙川县(应为淅川县)、汩罗市(应为汨罗市)、黑龙扛省、新疆维吾尔自区 |
规则 0 按 Id 特殊绑定 → 极少数 Location 不完整的个案(如水洞沟遗址)
规则 c 跨省大型遗产 → 长城、京杭大运河、茶马古道等 → City='跨省'
规则 s 新疆生产建设兵团 → 特殊处理
规则 a 直辖市 → City = Province
规则 A 当代市名匹配 → 直接采用
规则 B 当代县名 → 父市 → 通过 parent_code 回溯
规则 d 历史地名字典 → 约 150 条 (省,老名) → (市,新区县)
规则 e 多地并列 → 拆分后分别匹配,分号";"拼接
处理错别字省名(不止匹配当代标准名,也匹配已知错别字变体):
PROVINCE_ALIAS = {
'新疆维吾尔自区': '新疆维吾尔自治区',
'黑龙扛省': '黑龙江省',
}
strip_province() 在剥省名前缀时会反向遍历所有映射到目标省的错别字,确保 "黑龙扛省绥滨县" 也能正确剥离。
# '安徽省省阜南县' → 剥'安徽省'后剩'省阜南县' → 自动再剪一层 → '阜南县'
if rest.startswith('省') or rest.startswith('市'):
rest = rest[1:]
| 老地名(典型) | 当代归属 | 类型 |
|---|---|---|
| 陕西省长安县 | 西安市长安区 | 撤县设区 |
| 四川省大足县 | 重庆市大足区 | 跨省调整 |
| 河北省蓟县 | 天津市蓟州区 | 跨省调整 |
| 四川省涪陵市 | 重庆市涪陵区 | 跨省调整 |
| 贵州省黔西县 | 毕节市黔西市 | 撤县设市 |
| 山东省长岛县 | 烟台市蓬莱区 | 撤县并区 |
| 广西三江县 | 柳州市三江侗族自治县 | 自治县全称 |
| 河南省浙川县 | 南阳市淅川县 | 错别字修正 |
| 湖南省汩罗市 | 岳阳市汨罗市 | 错别字修正 |
| 海南省西、南、中沙群岛办事处 | 三沙市 | 2012 新设地级市 |
完整字典见 增强回填City.py 中 HISTORICAL_CITY 常量。
| 来源 | 数量 | 占比 | 说明 |
|---|---|---|---|
| A 当代市名匹配 | 2746 | 54.4% | Location 已含当代市名 |
| B 当代县名→父市 | 1698 | 33.6% | 通过 parent_code 回溯 |
| d_历史字典 | 331 | 6.6% | 老地名映射 |
| a_直辖市 | 261 | 5.2% | 北京/上海/天津/重庆 |
| c_跨省遗产 | 13 | 0.3% | 长城等大型遗产 |
| special_兵团 | 2 | 0.04% | 新疆生产建设兵团 |
| special_byid | 1 | 0.02% | 水洞沟遗址(Id=3290) |
零未命中,388 条 C 类全部收敛。
python 落库City字段.py 仅预览,只读不改库--apply 并手动输入 yes 才真正执行national_sites.db.bak.YYYYMMDD_HHMMSSALTER TABLE NationalHistoricalandCulturalSites ADD COLUMN City TEXT;
-- 批量 UPDATE 5052 条
CREATE INDEX idx_city ON NationalHistoricalandCulturalSites(City);
| 排名 | 城市 | 数量 |
|---|---|---|
| 1 | 北京市 | 132 |
| 2 | 运城市 | 102 |
| 3 | 郑州市 | 81 |
| 4 | 长治市 | 73 |
| 5 | 晋城市 | 72 |
| 6 | 晋中市 | 69 |
| 7 | 保定市 | 66 |
| 8 | 重庆市 | 64 |
| 9 | 苏州市 | 61 |
| 10 | 渭南市 | 60 |
| 11-20 | 西安 / 南京 / 临汾 / 洛阳 / 赤峰 / 黄山 / 张家口 / 杭州 / 泉州 / 上海 | 40~57 |
山西省三市(运城 / 长治 / 晋城)同时进入 Top 6,印证"中国古建看山西"的常识,数据质量可信。
数据库层到位后,Flask 侧同步升级:
| 文件 | 改动 |
|---|---|
routes.py |
overview() 补 city_stats Top 20;list_sites() 加 city 查询参数 + 联动下拉 city_options;show_site() 同步;搜索 LIKE 条件追加 City 字段 |
list.html |
新增"市"列;"按市"下拉(选定省后联动出现,带计数);per_page / 分页 / 详情链接全部携带 city 参数 |
index.html |
新增"按城市分布 Top 20"柱状图(绿色渐变,点击跳转按市筛选);搜索框 placeholder 补"城市" |
修复前搜"韶关"仅 3 条(Location 字面含"韶关市"),修复后 4 条——新增"满堂围"(Location=广东省始兴县,City=韶关市)。这是 City 字段既服务展示也打通搜索的闭环价值。
admin_division.db 可复用于其他子系统(MeteoGeo、HisWLD 等)parent_code 自引用足以表达三级树d:\BYP\Project\BYP\BYPsNotes\db\)admin_division.db — 行政区划基础库(3209 条)national_sites.db — 已加 City 字段 + idx_city 索引national_sites.db.bak.YYYYMMDD_HHMMSS — 落库前自动备份