用re.findall配合分层命名组和地名词典校验比单靠正则更可靠;纯正则无法解决歧义,必须加业务规则兜底,因地址不规范、顺序乱、同名干扰多,需两级正则+词典验证。

直接说结论:用 re.findall 配合分层命名组 + 地名词典辅助校验,比单靠正则硬匹配更可靠;纯正则无法解决歧义(比如“海淀”可能是区也可能是路名),必须加业务规则兜底。
为什么不能只写一个大正则匹配所有省市区?
地址文本太不规范:可能缺级、顺序乱(“北京市朝阳区建国路8号” vs “建国路8号,朝阳区,北京”)、夹杂括号/顿号/空格,甚至出现同名干扰(“江苏路”不是江苏省,“吉林大街”不是吉林省)。单纯靠 re.search(r'(.*?省)(.*?市)(.*?区)') 会大量漏匹配或错匹配。
常见错误现象:re.search 返回 None;匹配到“广东省深圳市南山区科技园”里的“广东省深圳市南山区”,但把“科技园”误当“区”;或把“河北东路”里的“河北”当成省名。
- 省名必须限定在权威列表里(如民政部公布的34个省级行政区),不能靠字面“X省”“X自治区”泛匹配
- 市名要区分地级市和县级市,例如“义乌市”是金华代管的县级市,不能脱离“金华”单独提取为“市”级
- 区名常与道路、小区名重叠,必须结合前置市名判断是否构成有效行政区划
推荐做法:两级正则 + 外部词典验证
先用宽泛正则抓出所有疑似省/市/区片段,再用预加载的行政区划树做路径校验。Python 中可用 re.findall 提取候选,再用字典快速查表。
示例流程:
import re
<h1>粗粒度提取(不保证准确,但尽量不漏)</h1><p>pattern = r'([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使港澳]{1,5}?[省市自治区]|[京津沪渝]市)'
text = "收货地址:上海市浦东新区张江路123号,近龙阳路地铁站"
matches = re.findall(pattern, text)</p><h1>→ ['上海市', '浦东新区']</h1>
注意:re.findall 返回字符串列表,不是 Match 对象;若需位置信息,改用 re.finditer。
- 省/市/区正则中,
[京津沪渝]单独列出,避免被“重庆北路”等干扰 - 不写
.*?贪婪匹配,防止跨字段吞掉关键分隔符 - 匹配后必须查行政区划表——比如“浦东新区”合法,“张江路”不合法,“龙阳路”直接剔除
实际提取时怎么组织层级关系?
地址结构不是扁平的,必须还原“省→市→区”嵌套关系。不能假设文本顺序就是逻辑顺序,得靠词典反向推导。
比如输入“杭州市西湖区文三路456号”,re.findall 可能返回 ['杭州市', '西湖区'],这时需确认:“西湖区”是否属于“杭州市”管辖(是);“文三路”是否为区名(否,跳过)。
- 用
dict或trie存储省→市→区三级映射,查询复杂度 O(1)~O(k) - 若只匹配到“西湖区”,但没提“杭州”,应尝试向上补全省市(查表得“西湖区”隶属“杭州市”,再查“杭州市”隶属“浙江省”)
- 遇到“朝阳区”,必须结合上下文判断:前面有“北京市”就取;只有“朝阳区”且无其他市名,则需 fallback 到默认市(如北京、沈阳、石家庄都有朝阳区,得看高频使用场景)
容易被忽略的关键点
真实地址里藏着大量非标准写法:缩写(“粤BXXXXX”里的“粤”)、旧称(“徽州”已改黄山)、多音字(“六安”读 lù ān)、生僻字(“歙县”“黟县”),还有港澳台特殊表述(“新界”“离岛”不属于内地三级结构)。
这些没法靠正则覆盖,必须单独建映射表。例如:
alias_map = {
"徽州": "黄山市",
"六安": "六安市",
"石河子": "新疆维吾尔自治区直辖县级市" # 不归地级市管
}
最后提醒:别试图用一个正则解决全部问题。先用 re.findall 做初筛,再用结构化词典做终审,中间留好日志打点——哪条地址卡在了“找不到对应市”,哪条因“朝阳区”歧义人工复核,这些才是后续优化的真实依据。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











