python爬虫:地理编码

0.144字数 923阅读 738

背景音乐:


背景

平时在做数据分析的时候,我们往往只能拿到地址信息,并不方便直接进行可视化。

我们需要将地址转成经纬度坐标,国内的高德、百度等地图服务商们都有提供现成的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。

三方面原因:

  1. 服务配额(日调用量上限)
    一般有两种类型的用户——个人型和企业型,这两种用户的服务配额是不同的。但无论是对于哪类用户,百度提供的服务配额量都比高德的要少很多,如果请求量较大的话,百度很有可能不能满足你的需求。

  2. 坐标加密
    高德提供的坐标是火星坐标(GCJ-02坐标,又称国测局坐标,谷歌、腾讯和高德都在用这个坐标体系),而百度有自己的坐标系,提供的坐标是加密过的坐标,并且不提供逆转服务,这个就很蛋疼了,这意味着如果将你的坐标投影在任何除了百度以外的地图上,都会偏离其真实的位置,以前我因为这个被坑惨了……详情可参考国内各地图API坐标系统比较与转换

  3. 开发者文档
    这个看体验,我问过的开发者都表示,高德甩百度好几条街。开发者文档友好不友好,谁用谁知道……

最后,感谢百度地图API,让我内心变得强大 T T

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
  • 刻意与不舍之间有太多不确定项,虽然,彪悍的过往在梦中也无济于事,顺带被人打死。 确切来说,并不可笑,抱着那具尸体,...
  • 这次心得我想谈谈我自我的剖析,从二月开始萱宝开始咳嗽到四月底我跟萱宝都在医院转,医生开了一大堆药,但是没有什么效果...
  • 今天晚上大家交了笔记丶不是为了写而写.有人写的很好,有的是对付。是从写的过程中突然闪过脑海中的灵光,对自己有帮助的...