PySpark中使用Libpostal(pypostal)提取地址街道与国家及数组元素获取异常问题
解决libpostal-pypostal在Spark UDF中的两个常见问题
嘿,我刚好之前也在Spark里用过libpostal,你的问题我太熟悉了!咱们一步步来拆解解决:
先理清楚你的核心问题
你要做的是用libpostal解析地址,提取街道(road)和国家(country)信息,过程中遇到两个头疼的问题:
- 曾出现返回类型为
net.razorvine.pickle.objects.ClassDictConstructor的序列化异常 - 尝试返回解析数组的第一个元素时,得到的是
null而非预期的街道名称(比如franklin ave)
第一个问题:奇怪的序列化异常
这个坑我也踩过!原因很直白:Spark的Python UDF在序列化返回值时,对Python原生的复杂对象(比如libpostal返回的元组列表)兼容性很差。你一开始要么直接返回原始解析结果,要么转成字符串返回,这些都不是Spark能直接识别的标准类型,所以就抛出了这个看起来莫名其妙的序列化异常。
解决办法:给UDF明确指定Spark能识别的返回类型,比如你后来用的array<string>,如果需要更复杂的结构还可以用struct,绝对不要返回Python原生的复杂对象(比如元组列表)。
第二个问题:返回数组第一个元素却得到null
这个是典型的「返回类型声明和实际返回值不匹配」的低级错误!你仔细看:
- 你把UDF的返回类型声明成了
array<string>(数组类型) - 但你实际返回的是
address_parsed[0],这是一个单独的字符串
Spark收到字符串后,尝试把它转换成数组类型,转换失败就只能返回null了!
正确写法来了
如果你只想要第一个元素(也就是街道名称),把UDF的返回类型改成string就行,另外最好加个非空判断,避免遇到没有匹配到road的地址时触发索引越界错误:
@udf("string") def parse(address): from postal.parser import parse_address address_parsed = [a[0] for a in parse_address(address) if a[1] in ['road', 'country']] # 防止数组为空时报错 return address_parsed[0] if address_parsed else None
这样改完,就能正常返回franklin ave这类街道名称了!
再给你确认下之前的正确代码
你写的返回包含road和country的数组那段代码是完全没问题的:
@udf("array<string>") def parse(address): from postal.parser import parse_address address_parsed = [a[0] for a in parse_address(address) if a[1] in ['road', 'country']] return address_parsed
这里返回类型是array<string>,实际返回的也是字符串数组,类型完全匹配,所以结果完全符合预期。
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

