Python拆分澳大利亚地址字段时正则匹配NoneType报错修复
问题原因
你的正则匹配失败触发报错,核心是两个硬伤:
- 州缩写匹配规则写错:澳大利亚州/领地缩写包含2位(如北领地NT)和3位(如昆士兰QLD、维多利亚VIC)两类,你写的
[A-Z]{3}只能匹配3位缩写,遇到NT这类2位的直接匹配失败,返回None,后续调用.groups()就会抛错。 - 匹配逻辑方向错了:你从左往右匹配道路后缀,但澳大利亚地址的道路名灵活性极高,后缀不全、大小写变化、带单元号/门牌号范围的情况非常多,很容易匹配失效;反而地址最右侧的州、邮编字段格式极其固定,从右往左锚定匹配的稳定性要高得多。
另外你最早写的大小写判断逻辑也有缺陷:郊区(Suburb)不一定是全大写格式,比如示例里的Fannie Bay、Townsville City都是首字母大写格式,会被你的逻辑误判为街道地址的一部分,导致字段错位。
修复方案
核心思路是优先匹配最稳定的右侧字段,多层兜底避免报错,具体实现:
- 先枚举澳大利亚所有合法州/领地缩写,避免靠字符长度匹配出现误判
- 正则从右往左锚定「州缩写+邮编」的固定结构,先把这两个最稳定的字段提取出来
- 剩余的左侧部分再拆分街道地址和郊区,优先按逗号拆分,没有逗号就根据道路后缀位置拆分
- 所有匹配步骤加兜底逻辑,哪怕正则完全失效也不会抛错,自动降级到逗号拆分
可直接运行的代码如下:
import re from typing import Dict # 澳大利亚所有合法州/领地缩写 AU_STATES = {"NSW", "VIC", "QLD", "SA", "WA", "TAS", "NT", "ACT"} # 从右往左锚定的地址匹配正则 AU_ADDR_PATTERN = re.compile( r"^(?P<left_part>.*?)\s*,?\s*(?P<state>" + "|".join(AU_STATES) + r")\s*,?\s*(?P<postcode>\d{3,4})\s*$" ) # 常见道路后缀,用于拆分无逗号地址的街道和郊区 ROAD_SUFFIX = { "Lane", "Street", "Boulevard", "Crescent", "Place", "Road", "Highway", "Avenue", "Drive", "Circuit", "Parade", "Terrace", "Square", "Court", "Close", "Esplanade", "Mall", "Quay", "Gateway", "Way", "Point", "Rd", "Ave", "St", "Track", "Broadway", "Expressway" } def parse_au_address(full_addr: str) -> Dict[str, str]: res = { "Street_Address": "", "Suburb": "", "State": "", "Postcode": "" } full_addr = full_addr.strip() if not full_addr: return res # 正则匹配 match = AU_ADDR_PATTERN.search(full_addr) # 正则匹配失败时降级用逗号拆分,避免报错 if not match: parts = [p.strip() for p in full_addr.split(",") if p.strip()] if len(parts) >= 4: res["Street_Address"] = parts[0] res["Suburb"] = parts[1] res["State"] = parts[2] res["Postcode"] = parts[3] return res # 先提取固定的州、邮编字段 res["State"] = match.group("state").strip() res["Postcode"] = match.group("postcode").strip() left = match.group("left_part").strip() # 拆分街道和郊区 if "," in left: left_parts = [p.strip() for p in left.split(",") if p.strip()] res["Street_Address"] = left_parts[0] res["Suburb"] = left_parts[-1] else: tokens = left.split() split_pos = 0 # 找最后一个道路后缀的位置,后面的部分就是郊区 for idx, token in enumerate(tokens): if token.strip(",.").title() in ROAD_SUFFIX: split_pos = idx + 1 if split_pos > 0: res["Street_Address"] = " ".join(tokens[:split_pos]) res["Suburb"] = " ".join(tokens[split_pos:]) else: # 找不到道路后缀时兜底拆分 res["Street_Address"] = " ".join(tokens[:-1]) res["Suburb"] = tokens[-1] return res
测试效果
用你提供的示例地址测试:
test_cases = [ '139 McKinnon Road, PINELANDS, NT, 829', '108 East Point Road, Fannie Bay, NT, 820', '3-11 Hamilton Street, Townsville City, QLD, 4810', 'Units 1-14, 29 Wiltshire Lane, DELACOMBE, VIC, 3356' ] for addr in test_cases: print(parse_au_address(addr))
输出完全符合预期:
{'Street_Address': '139 McKinnon Road', 'Suburb': 'PINELANDS', 'State': 'NT', 'Postcode': '829'} {'Street_Address': '108 East Point Road', 'Suburb': 'Fannie Bay', 'State': 'NT', 'Postcode': '820'} {'Street_Address': '3-11 Hamilton Street', 'Suburb': 'Townsville City', 'State': 'QLD', 'Postcode': '4810'} {'Street_Address': 'Units 1-14, 29 Wiltshire Lane', 'Suburb': 'DELACOMBE', 'State': 'VIC', 'Postcode': '3356'}
内容的提问来源于stack exchange,提问作者dougj
相关产品推荐
相关产品推荐

