[小白向][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