[小白向][Python]使用高德API进行地址编码
- 2021 年 5 月 17 日
- AI
太长不看说明
需要使用相关程序可以直接看最后的“程序整体”一节。
前言
@李国宝 宝哥说得好,菜是原罪。
在这里要向我的关注者们说明一下,目前阶段我的代码能力极弱,辜负了你们的信任。自转专业以来,我一直想着有问题可以靠使用 MATLAB 的现成工具去解决,也没动手写一下代码,临找实习和做实验的时候才发现自己没有这方面的能力。
不论是做研究还是做工作,其实都和做人一样讲究“知行合一”,既要有理论指引,也应该有动手能力。过去我一直想着和学习数学一样,从头开始,从全面到细致的角度去学习计算机,而又因为执行力的原因总是半途而废。
现在我发现,编程方面较好的学习方法还是根据个人需要(学习、研究、工作)设定项目,然后自己从头开始执行项目,等到项目完成了,也有了实践经验。
项目说明
项目需求
因工作需求,我需要从地址信息中抽取【省市区】信息,数据量大约有5W条,有严重的数据缺失和不规范现象。
方案设计
以往的做法是将数据拆分,分给每个人进行处理(每个人大约处理500条信息),这样大约需要100个人参与这项工作。评估下来较为费时费力。
经过与需求方的讨论,需求方预想了两个可执行方案:
- 在EXCEL中设置VBS宏对地址进行校验(是否有省市区信息,省市区对应关系是否正确);
- 通过数据校验在EXCEL中设置省市区三列,第一列全国各省列表,第二列全国各市列表…(这里无法校验省市区对应关系是否正确,仅能确保对应字段有意义)。
我设想了三个方案:
- 在具有三级地址输入的问卷网站,完成地址信息的重新录入并导出数据;
- 通过字符长度或者正则表达式,从原地址数据中拆分出省市区信息;
- 调用相关API接口,完成从地址数据到结构化地址数据的转化。
结合个人能力和性价比方面的考虑,初步选择使用调用API帮助完成工作(后面验证这个方法确实很方便)
方案验证
通过查询,了解到根据现有的无格式地址数据生成对应格式化的地址数据,这个应该是“地址清洗”的工作。有很多网站与平台提供相应服务,价格大概在100次/元以下[1][2]。
后经 @李国宝 老师的指导,告诉我高德提供“地理编码”服务[3],个人认证用户可以每天免费使用30W次[4],处理效果也很好。


编程处理
需求分析
根据前面所述,确定需要批量调用高德API,且还涉及到文件读写的功能。
功能模块
调用高德API完成地址编码并返回结果
本段为 @李国宝 用2分钟帮我写的第1版本,已实现读取CSV文件,调用高德API完成地址编码并返回地址编码结果。
Python3"># 宝哥花2分钟写的内容,已完全解决核心需求
import requests
amap_key = "amap_key"
def get_address_info(address):
url = f"//restapi.amap.com/v3/geocode/geo?key={amap_key}&address={address}"
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
return response.json()
all_address = []
with open("xxxxx.csv", "r+") as xx_fp:
for onle_line in xx_fp.readlines():
address_info = get_address_info(onle_line)
all_address.append(address_info)
print(all_address)
处理数据内容
可以看到返回的JSON数据有多层。
特别需要注意可能出现没有‘geocodes’字段及其子级字段的情况。
(在单条数据请求中,当’count’字段取值为’1’时,才会有’geocodes’的内容)


返回JSON格式数据,根据需求处理为字典列表,再输出为CSV。
# 设置地址json数据转dict格式函数
def oneline_geo_json2dict(address_info):
address_info_dict={}
# 当'count'字段取值为'1'时,才会有'geocodes'的内容
# if len(address_info.get('geocodes',[]))!=0:
if address_info.get('count',[])=='1':
address_info_dict['formatted_address'] = address_info['geocodes'][0]['formatted_address']
address_info_dict['country'] = address_info.get('geocodes')[0].get('country')
address_info_dict['province'] = address_info.get('geocodes')[0].get('province')
address_info_dict['city'] = address_info.get('geocodes')[0].get('city')
address_info_dict['district'] = address_info.get('geocodes')[0].get('district')
address_info_dict['lng'] = address_info.get('geocodes')[0].get('location').split(',')[0]
address_info_dict['lat'] = address_info.get('geocodes')[0].get('location').split(',')[1]
else:
address_info_dict['formatted_address'] = ''
address_info_dict['country'] = ''
address_info_dict['province'] = ''
address_info_dict['city'] = ''
address_info_dict['district'] = ''
address_info_dict['lng'] = ''
address_info_dict['lat'] = ''
return address_info_dict
程序主体
调用前述地址编码和格式转换函数,再加上文件读写操作。
程序整体
以下代码经测试可以运行。
需要先申请高德webAPI key,然后将得到的key赋值到 ‘amap_key’ 变量。

需要准备 ‘inputdata.csv’ 文件,文件第一行为表头,第一列为编号,第二列为地址。

# 调用高德地址编码服务,实现将地址文本转换为结构化地址数据功能
# API文档 //lbs.amap.com/api/webservice/guide/api/georegeo
# 输入文件 inputdata.csv 文件第一行为表头,第一列为编号,第二列为地址
# 输出文件 test_main.json 查询后的结构化地址信息JSON文件
# 输出文件 outputdata_main.csv 输入文件及结构化地址信息合并后,输出为CSV文件
# via 白小鱼 //github.com/youngfish42
#### 初始化
import requests
import json
import time
import csv
# 高德webAPI key 申请链接 //lbs.amap.com/dev/key
amap_key = "amap_key"
# 设置地址编码函数
def get_formatted_address(address):
url = f"//restapi.amap.com/v3/geocode/geo?key={amap_key}&address={address}" # f加字符串处理后,可将{}内的值完成赋值
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
return response.json()
# 设置地址json数据转dict格式函数
def oneline_geo_json2dict(address_info):
address_info_dict={}
# 当'count'字段取值为'1'时,才会有'geocodes'的内容
# if len(address_info.get('geocodes',[]))!=0:
if address_info.get('count',[])=='1':
address_info_dict['formatted_address'] = address_info['geocodes'][0]['formatted_address']
address_info_dict['country'] = address_info.get('geocodes')[0].get('country')
address_info_dict['province'] = address_info.get('geocodes')[0].get('province')
address_info_dict['city'] = address_info.get('geocodes')[0].get('city')
address_info_dict['district'] = address_info.get('geocodes')[0].get('district')
address_info_dict['lng'] = address_info.get('geocodes')[0].get('location').split(',')[0]
address_info_dict['lat'] = address_info.get('geocodes')[0].get('location').split(',')[1]
else:
address_info_dict['formatted_address'] = ''
address_info_dict['country'] = ''
address_info_dict['province'] = ''
address_info_dict['city'] = ''
address_info_dict['district'] = ''
address_info_dict['lng'] = ''
address_info_dict['lat'] = ''
return address_info_dict
#### 数据输入
all_formatted_addr = []
all_formatted_addr_dict = []
with open("./inputdata.csv", "r+",encoding='utf-8-sig') as input_data:
# inputdata.csv 文件第一列为编号,第二列为地址,此处读取第二列的“地址”数据并进行地址编码
inputcsv = csv.DictReader(input_data) #将CSV文件每行数据读取为字典对象
for addr_info_origin in inputcsv:
# print(one_line) #测试某条输入数据
formatted_addr = get_formatted_address(addr_info_origin['地址'])
formatted_addr_dict = oneline_geo_json2dict(formatted_addr)
all_formatted_addr.append(formatted_addr)
all_formatted_addr_dict.append(dict(addr_info_origin,**formatted_addr_dict)) #将原始数据和地址编码后数据合并,并存入字典列表
time.sleep(0.01) #控制并发量(上限每秒200次请求)
#### 数据输出
# 测试全部数据的输出
#print(all_formatted_addr)
# 将JSON写入文件
with open('test_main.json', 'w', encoding='UTF-8') as output_data:
json.dump(all_formatted_addr, output_data, ensure_ascii=False)
# 将字典内容写入CSV文件
csv_columns = ['编号','地址','formatted_address', 'country', 'province','city','district','lng','lat']
try:
with open("outputdata_main.csv", 'w',newline="") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=csv_columns)
writer.writeheader()
for data in all_formatted_addr_dict:
writer.writerow(data)
except IOError:
print("I/O error")
随手输入地址得到结构化地址版本[5]
import requests
import json
address = input('请输入地点:')
par = {'address': address, 'amap_key': 'amap_key'}
url = '//restapi.amap.com/v3/geocode/geo'
res = requests.get(url, par)
json_data = json.loads(res.text)
json_data = res.json()
geo = json_data['geocodes'][0]['location']
lng = geo.split(',')[0]
lat = geo.split(',')[1]
print(json_data['geocodes'][0]['country'])
print(json_data['geocodes'][0]['province'])
print(json_data['geocodes'][0]['city'])
print(json_data['geocodes'][0]['district'])
print(lng,lat)

下一步改进
- 数据验证。在输入数据阶段就执行数据验证,如果遇到空的地址信息就不传给调用API了。
- 提高批量处理能力。该API可以每次同时处理10条地址数据,之后或许可以从这个角度提升处理效率。
但目前阶段需要处理的数据一共5W条,几年才处理一次,API每天可以处理30W条,需求不大,先搁置一下…
感谢
感谢以@李国宝老师为代表的朋友们的支持, 虽然代码也就几行,但我在不了解Python语法的情况下走了很多弯路,问了他们很多奇奇怪怪的初学者的问题。
另外在这个过程中一些细节参考了文档(已列在参考资料部分)和两本书籍(文后推荐)。
一直以来害怕编程,终于写了个小demo感觉莫名的开心~(虽然还是很菜…
果然消除恐惧的最好办法就是面对恐惧,坚持,才是胜利!加油,奥利给!
参考
- ^中文地址识别api的使用测试,快递地址自动补全,自动识别省市区,地址清洗,到底哪个好用? //blog.csdn.net/Afterwards_/article/details/105905149
- ^智能地址解析,物流快递中文地址识别格式化补全 //market.cloud.tencent.com/products/19581
- ^地理/逆地理编码-API文档-开发指南-Web服务 API | 高德地图API //lbs.amap.com/api/webservice/guide/api/georegeo
- ^流量限制说明-实用工具-开发指南-Web服务 API | 高德地图API //lbs.amap.com/api/webservice/guide/tools/flowlevel
- ^【Python_地理编码】高德地理编码简易实现 //blog.csdn.net/YWP_2016/article/details/115488099