一、前言
在项目开发过程中,经常会遇到只有 GPS 经纬度,没有具体地址的数据,例如车辆轨迹、人员定位、设备定位等。
如下图所示,Excel 中保存了经纬度信息:
| GIS_LON | GIS_LAT | GIS_LON_O | GIS_LAT_O |
|---|---|---|---|
| 121.473701 | 31.230416 | 121.473680 | 31.230390 |
希望最终生成:
| GIS_LON | GIS_LAT | GIS_LON_O | GIS_LAT_O | 完整地址 | 省 | 市 | 区县 | 街道 |
|---|---|---|---|---|---|---|---|---|
| 121.473701 | 31.230416 | 121.473680 | 31.230390 | 上海市黄浦区人民大道100号 | 上海市 | 上海市 | 黄浦区 | 南京东路街道 |
本文介绍如何使用 Python + 高德地图 Web 服务 API 实现 Excel 经纬度批量解析中文地址。
二、准备工作
1、申请高德地图 Key
访问高德开放平台:
注册开发者账号。
进入:
text
控制台
↓
我的应用
创建应用时,应用类型必须选择:
text
Web 服务(Web Service)
注意:
不要创建 Android、iOS 或 Web 端(JS API) Key,否则调用接口会报:
textUSERKEY_PLAT_NOMATCH infocode:10009
创建完成后,复制生成的 Key,例如:
text
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、安装依赖
安装所需 Python 库:
bash
pip install pandas requests openpyxl tqdm
四、高德逆地理编码接口
接口地址:
text
https://restapi.amap.com/v3/geocode/regeo
请求示例:
text
https://restapi.amap.com/v3/geocode/regeo?key=你的Key&location=121.473701,31.230416
返回结果:
json
{
"status":"1",
"regeocode":{
"formatted_address":"上海市黄浦区人民大道100号",
"addressComponent":{
"province":"上海市",
"city":"上海市",
"district":"黄浦区",
"township":"南京东路街道"
}
}
}
五、Python 完整代码
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import requests
import pandas as pd
from tqdm import tqdm
# ======================================================
# 配置
# ======================================================
# 高德Key
AMAP_KEY = ""
# 输入文件
INPUT_FILE = "0012.xlsx"
# 输出文件
OUTPUT_FILE = "baidu-gis-out.xlsx"
# 是否使用原始坐标(N、O)
USE_ORIGINAL = False
# 高德API
API = "https://restapi.amap.com/v3/geocode/regeo"
# ======================================================
# 缓存
# ======================================================
cache = {}
# ======================================================
# 查询地址
# ======================================================
def reverse_geocode(lon, lat):
key = f"{lon},{lat}"
if key in cache:
return cache[key]
params = {
"key": AMAP_KEY,
"location": key,
"extensions": "all",
"radius": 100
}
try:
r = requests.get(API, params=params, timeout=10)
data = r.json()
if data["status"] != "1":
print(data)
return {}
regeocode = data["regeocode"]
component = regeocode.get("addressComponent", {})
result = {
"地址": regeocode.get("formatted_address", ""),
"省": component.get("province", ""),
"市": component.get("city", ""),
"区县": component.get("district", ""),
"街道": component.get("township", ""),
"门牌号": component.get("streetNumber", {}).get("number", ""),
"POI": ""
}
pois = regeocode.get("pois", [])
if len(pois) > 0:
result["POI"] = pois[0].get("name", "")
cache[key] = result
return result
except Exception as e:
print("查询失败:", key, e)
return {}
# ======================================================
# 主程序
# ======================================================
print("读取Excel...")
df = pd.read_excel(INPUT_FILE)
query_lon = []
query_lat = []
address = []
province = []
city = []
district = []
township = []
street = []
poi = []
print("开始解析地址...")
for _, row in tqdm(df.iterrows(), total=len(df)):
if USE_ORIGINAL:
lon = row["GIS_LON_O"]
lat = row["GIS_LAT_O"]
else:
lon = row["GIS_LON"]
lat = row["GIS_LAT"]
query_lon.append(lon)
query_lat.append(lat)
if pd.isna(lon) or pd.isna(lat):
address.append("")
province.append("")
city.append("")
district.append("")
township.append("")
street.append("")
poi.append("")
continue
result = reverse_geocode(lon, lat)
address.append(result.get("地址", ""))
province.append(result.get("省", ""))
city.append(result.get("市", ""))
district.append(result.get("区县", ""))
township.append(result.get("街道", ""))
street.append(result.get("门牌号", ""))
poi.append(result.get("POI", ""))
# 防止QPS过高
time.sleep(0.05)
# ======================================================
# 新增列
# ======================================================
df["查询经度"] = query_lon
df["查询纬度"] = query_lat
df["完整地址"] = address
df["省"] = province
df["市"] = city
df["区县"] = district
df["街道"] = township
df["门牌号"] = street
df["POI"] = poi
# ======================================================
# 保存
# ======================================================
df.to_excel(OUTPUT_FILE, index=False)
print()
print("======================================")
print("完成!")
print("输出文件:", OUTPUT_FILE)
print("共解析:", len(df), "条")
print("缓存命中:", len(cache), "个唯一坐标")
print("======================================")
六、运行效果
原始 Excel:
| GIS_LON | GIS_LAT |
|---|---|
| 121.473701 | 31.230416 |
运行程序:
text
开始解析...
100%|████████████████████|161585/161585
解析完成!
输出文件:坐标_解析结果.xlsx
生成新的 Excel:
| GIS_LON | GIS_LAT | 查询经度 | 查询纬度 | 完整地址 | 省 | 市 | 区县 | 街道 |
|---|---|---|---|---|---|---|---|---|
| 121.473701 | 31.230416 | 121.473701 | 31.230416 | 上海市黄浦区人民大道100号 | 上海市 | 上海市 | 黄浦区 | 南京东路街道 |
七、常见问题
1、ImportError: cannot import name 'tqdm'
原因:
脚本命名为:
text
tqdm.py
Python 导入时会优先加载当前目录,导致循环引用。
解决方法:
修改脚本名称,例如:
text
reverse_geocode.py
同时删除:
text
__pycache__
2、USERKEY_PLAT_NOMATCH(10009)
错误:
text
USERKEY_PLAT_NOMATCH
原因:
创建了错误类型的 Key。
解决方法:
重新创建:
text
Web 服务(Web Service)
不要使用:
- Android
- iOS
- Web端(JS API)
3、为什么部分地址为空?
可能原因:
- 经纬度为空。
- 经纬度超出中国区域。
- 海上坐标。
- 高德暂无对应地址。
- 网络请求失败。
八、优化建议
如果数据量较大(例如几万甚至几十万条),建议增加以下优化:
- 缓存机制:相同坐标只查询一次,减少 API 请求次数。
- 失败重试:网络异常时自动重试,提升成功率。
- 限速控制:避免超过高德 API 的 QPS 限制。
- 断点续跑:程序中断后可继续执行,无需重新解析全部数据。
- 本地缓存:使用 SQLite 保存已解析结果,后续重复运行无需再次请求。
九、总结
本文介绍了如何使用 Python + Pandas + 高德地图 Web 服务 API,实现 Excel 经纬度批量转换中文地址。
整个流程包括:
- 申请高德 Web 服务 Key。
- 调用逆地理编码接口。
- 批量读取 Excel 中的经纬度。
- 将解析得到的地址信息写入新的 Excel 文件。
- 保留原始数据并新增地址字段。
对于一般业务场景,这套方案简单易用;如果需要处理几十万甚至上百万条数据,可以进一步增加缓存、重试、限速和断点续跑等能力,以满足生产环境的大规模数据处理需求。