python爬虫:地理编码
背景音乐:
背景
平时在做数据分析的时候,我们往往只能拿到地址信息,并不方便直接进行可视化。
我们需要将地址转成经纬度坐标,国内的高德、百度等地图服务商们都有提供现成的API接口,方便我们直接调用。
高德提供的Web服务高德API
以高德为例,查看官方文档里的地理/逆地理编码部分:
请求参数
请求参数官方文档提供了详细的说明,点赞!
可以看到,必要的参数只有key和address,其他参数都是可选的。
个人建议地址字符串前直接加上city,这样能定位得更加准确。
如何申请key可以参考文章高德地图_获取KEY的方法。
返回参数
返回参数大赞高德的说明文档,数据结构一目了然。
可以看到,发起请求后,会收到json格式的数据,里面包含了很多信息。
其中,geocoders是一个列表,里面包含了若干个较匹配的结果,按相关性排序。
大多数情况下高德解析得都准的,所以我们挑第一个数据,里面的location值正式我们要的。
地理编码
环境:python 2.7
系统:macOS 10.13.1
模块:requests
单地理编码
python脚本如下:
import requests
# 地理编码函数
def geocode(address):
url = 'http://restapi.amap.com/v3/geocode/geo'
params = {
'address':address,
'key':'你的key',
}
try:
res = requests.get(url, params, timeout=10)
location = res.json()['geocodes'][0]['location']
lng, lat = map(float, location.split(','))
except Exception, e:
print e
print 'address: {0:10s} can\'t be geocoded.'.format(address)
lng = lat = None
return lng, lat
# 处理正常地址
print geocode('上海市浦东区世纪大道1号')
# (121.500836, 31.239135)
# 处理异常地址
print geocode('几百斤鸭送到巴拿马')
# list index out of range
# address: 几百斤鸭送到巴拿马 can't be geocoded.
# (None, None)
脚本正确地解析出了给定地址的经纬度,对于我乱填的地址也做了正确的提示。
批量地址编码
如果要解析的地址比较多,最好按照官方文档所示的格式来进行批量请求。
毕竟每个key都有调用数量的限制(可以查阅流量限制说明),用for循环太浪费了。
截止到我发文章的时刻,一次请求最多同时支持10 个地址(用"|"分割)。
python脚本如下:
import requests
# 批量地理编码
def geocode_batch(address_list):
location_list = []
num_reqs = len(address_list) // 10 + 1
for req_index in range(num_reqs):
sl = slice(req_index * 10, (req_index + 1) * 10)
address_picked = address_list[sl] # 用slice提取列表元素
address = '|'.join(address_picked) # 用“|”拼接地址
url = 'http://restapi.amap.com/v3/geocode/geo'
params = {
'address':address,
'key':'你的key',
'batch':True # 要传batch参数
}
try:
res = requests.get(url, params, timeout=10)
for add, geo in zip(address_picked, res.json()['geocodes']):
if geo['location']: # 当地址错误时,该地址的location为空
location = map(float, geo['location'].split(','))
else:
print 'address: {} can\'t be geocoded.'.format(add)
location = [None] * 2 # 异常值用None代替
location_list.append(location)
except Exception, e:
print e
location_list += [[None, None]] * len(address_picked)
# 打印进度
print req_index + 1, '/', num_reqs, 'done!'
print 'all done!'
return location_list
# 处理正常地址列表,11个地址将会分两次请求(10+1)
address_list = [
'上海市浦东区世纪大道1号',
'上海虹桥火车站',
'上海浦东国际机场',
'上海市复旦大学',
'上海市同济大学',
'上海市蔚来汽车有限公司',
'北京市海淀区学院路30号',
'北京市奥林匹克森林公园',
'北京市王府井大街',
'北京市簋街',
'北京市清华大学'
]
print geocode_batch(address_list)
# 1 / 2 done!
# 2 / 2 done!
# all done!
# [[121.500836, 31.239135],
# [121.31895, 31.194022],
# [121.807559, 31.140815],
# [121.506998, 31.298877],
# [121.221467, 31.28835],
# [121.170009, 31.282628],
# [116.355288, 39.987847],
# [116.406004, 40.025139],
# [116.41131, 39.912619],
# [116.407526, 39.90403],
# [116.316533, 40.000727]]
# 当列表中有异常的地址
address_list = [
'北京市海淀区学院路30号',
'快乐多一点',
'北京市簋街'
]
print geocode_batch(address_list)
# address: 快乐多一点 can't be geocoded.
# 1 / 1 done!
# all done!
# [[116.355288, 39.987847],
# [None, None],
# [116.407526, 39.90403]]
测试了两个用例,脚本正确地对异常的地址进行了处理。
后记
百度地图API和高德地图API的对比
从我这一年多的使用经验来看,我一定会推荐用高德地图API。
三方面原因:
-
服务配额(日调用量上限)
一般有两种类型的用户——个人型和企业型,这两种用户的服务配额是不同的。但无论是对于哪类用户,百度提供的服务配额量都比高德的要少很多,如果请求量较大的话,百度很有可能不能满足你的需求。 -
坐标加密
高德提供的坐标是火星坐标(GCJ-02坐标,又称国测局坐标,谷歌、腾讯和高德都在用这个坐标体系),而百度有自己的坐标系,提供的坐标是加密过的坐标,并且不提供逆转服务,这个就很蛋疼了,这意味着如果将你的坐标投影在任何除了百度以外的地图上,都会偏离其真实的位置,以前我因为这个被坑惨了……详情可参考国内各地图API坐标系统比较与转换 -
开发者文档
这个看体验,我问过的开发者都表示,高德甩百度好几条街。开发者文档友好不友好,谁用谁知道……
最后,感谢百度地图API,让我内心变得强大 T T