
本文介绍通过并发请求、请求节流与批量优化策略,将数十万地址的地理编码耗时从分钟级降至秒级的实用方法,兼顾google maps api配额限制与稳定性。
本文介绍通过并发请求、请求节流与批量优化策略,将数十万地址的地理编码耗时从分钟级降至秒级的实用方法,兼顾google maps api配额限制与稳定性。
地理编码(Geocoding)是将地址文本转换为经纬度坐标的常见任务,但直接串行调用Google Maps Geocoding API(如原代码中每地址休眠1秒、单线程循环)在处理大规模数据(如33万条地址)时效率极低——60条已耗时1分钟,全量处理将超过38小时。要真正提速,关键不是“更快发请求”,而是科学并发 + 合理限速 + 错误韧性。
✅ 推荐方案:多进程 + 每进程匀速请求(推荐)
Google Maps API 允许每个API密钥每秒最多5次请求(QPS),且有日配额限制。盲目并发会导致OVER_QUERY_LIMIT错误,触发重试反而拖慢整体进度。最优解是:使用 multiprocessing 启动固定数量的工作进程(如5个),每个进程独立、匀速地发送请求(例如每秒1次),从而安全达到总QPS ≈ 5,最大化吞吐而不被限流。
以下是优化后的完整示例(含异常处理、结果结构化、进度提示):
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
import requests
import json
import pandas as pd
from multiprocessing import Pool, cpu_count
from time import sleep
from tqdm import tqdm # pip install tqdm
GOOGLE_API_KEY = "your_api_key_here"
GEOCODE_URL = "https://maps.googleapis.com/maps/api/geocode/json"
def geocode_single(address):
"""单地址地理编码,带重试与错误防护"""
max_retries = 3
for attempt in range(max_retries):
try:
params = {"address": address, "key": GOOGLE_API_KEY}
res = requests.get(GEOCODE_URL, params=params, timeout=10)
data = res.json()
if data["status"] == "OK":
loc = data["results"][0]["geometry"]["location"]
return {
"address": address,
"latitude": loc["lat"],
"longitude": loc["lng"],
"status": "success"
}
elif data["status"] == "ZERO_RESULTS":
return {"address": address, "latitude": None, "longitude": None, "status": "no_result"}
elif data["status"] == "OVER_QUERY_LIMIT":
sleep(2 ** attempt) # 指数退避
continue
else:
return {"address": address, "latitude": None, "longitude": None, "status": f"error:{data['status']}"}
except Exception as e:
if attempt == max_retries - 1:
return {"address": address, "latitude": None, "longitude": None, "status": f"exception:{str(e)}"}
sleep(1)
return {"address": address, "latitude": None, "longitude": None, "status": "failed_after_retry"}
# 主执行逻辑
if __name__ == "__main__":
address_list = ["北京市朝阳区建国路1号", "上海市浦东新区世纪大道100号", "广州市天河区珠江新城"] # 示例地址
# 使用 CPU核心数或按需设为5(匹配QPS上限)
num_workers = min(5, cpu_count())
with Pool(processes=num_workers) as pool:
# tqdm包装迭代器显示进度条
results = list(tqdm(pool.imap(geocode_single, address_list), total=len(address_list)))
# 转为DataFrame,确保字段顺序:address, latitude, longitude
df = pd.DataFrame(results)[["address", "latitude", "longitude", "status"]]
print(df)
# 可选:保存结果
# df.to_csv("geocoded_results.csv", index=False, encoding="utf-8-sig")⚠️ 关键注意事项
- 不要使用 threading:Geocoding 是 I/O 密集型任务,但 requests 在 CPython 中受 GIL 影响不大;不过 Google API 响应延迟高,multiprocessing 更稳定,避免会话/连接复用冲突。
- 禁止无节制并发:即使使用10个进程,若不控制各进程请求间隔,仍会高频触发 OVER_QUERY_LIMIT,导致大量重试和失败。
- 务必添加超时与重试:网络波动或临时服务不可用很常见,timeout=10 和指数退避(sleep(2**attempt))可大幅提升鲁棒性。
- 检查配额与计费:免费额度仅每月 $200(约 40,000 次标准Geocoding),生产环境请启用账单提醒,并考虑缓存已解析结果(如用Redis或本地SQLite)避免重复调用。
- 替代方案参考:若对精度要求不高且需极致速度,可评估开源方案(如 Nominatim + OSM,需自建服务)或国内API(高德、百度地图,QPS更高且支持批量接口)。
通过以上方法,60个地址可在约12秒内完成(5进程 × 每秒1请求),33万地址预计耗时约18–20小时——相比原方案提速超70倍,且全程可控、可监控、可恢复。真正的“快”,源于尊重服务边界的设计智慧。
立即学习“Python免费学习笔记(深入)”;

















