背景

高德地图提供了三种方式来获取POI数据:

  • 根据关键字进行搜索指定城市内的POI数据,比如爬取广州市的大学;
  • 爬取某个中心点一定半径范围内的数据;
  • 爬取某个多边形范围内的数据。

根据关键字搜索的可以参见目前实现的工具:http://www.mapboxx.cn/tool/poiview/ 本次打算实现第三种,根据多边形范围爬取POI数据。大概思路如下:

  • 获取需要爬取的城市边界坐标数据,并计算出最大经度、纬度和最小经度、纬度(为了把城市包括在一个大矩形范围内);
  • 根据一定的距离将大矩形划分为N多个小矩形范围,并获取其小矩形的边界坐标;
  • 调用高德地图POI,传入小矩形的边界坐标以及需要爬取的POI类型,即可得到所需数据

目前存在问题

  • 划分的网格数会随着指定的距离变化而变化,如果网格太多,爬取过程比较耗时,网格太少的话又有单个网格只能爬900条数据的风险(当然,理论上来说,只要网格划分得足够小,就能突破900条的限制);
  • 耗时比较久。

步骤:

1. 获取密钥

申请高德开放平台账号,并且申请开发者认证,然后申请web服务类型的密钥。

2. 获取城市最大和最小经纬度

使用的高德行政区划查询接口,地址:https://lbs.amap.com/api/webservice/guide/api/district

示例url:http://restapi.amap.com/v3/config/district?keywords=%E5%B9%BF%E5%B7%9E&key=4188efb67360681f89110ccdb11e563b&subdistrict=1&extensions=all

后续做JSON数据的解析,提取边界经纬度,并且使用math.max()和math.min()函数获取经纬度的最大和最小值。具体代码如下:

# 第一行必须有,否则报中文字符非ascii码错误
import urllib.request
from urllib.parse import quote
import json


#TODO 1
# 高德上申请的key
key = '4188efb67360681f89110ccdb11e563b' # 需替换为自己的
# TODO 2 搜索的城市名(全名)
addr_name = '广州'


url = 'http://restapi.amap.com/v3/config/district?'
def getlnglat(address):

    uri = url + 'keywords=' + quote(address) + '&key=' + key + '&subdistrict=1' + '&extensions=all'

    # 访问链接后,api会回传给一个json格式的数据
    temp = urllib.request.urlopen(uri)

    temp = json.loads(temp.read())


    # polyline是坐标,name是区域的名字
    Data = temp["districts"][0]['polyline']

    lngs = []
    lats = []
    points = []
    for line in str(Data).split(";"):
        if len(line.split("|")) > 1:
            for uu in line.split("|"):
                if float(uu.split(",")[0]) != None:
                    lngs.append(float(uu.split(",")[0]))
                    lats.append(float(uu.split(",")[1]))
                    points.append([float(uu.split(",")[0]), float(uu.split(",")[1])])
        else:
            if float(line.split(",")[0]) != None:
                lngs.append(float(line.split(",")[0]))
                lats.append(float(line.split(",")[1]))
                points.append([float(line.split(",")[0]), float(line.split(",")[1])])

    print(points)
    print(max(lngs), min(lngs), max(lats), min(lats))
    return max(lngs), min(lngs), max(lats), min(lats)

getlnglat(addr_name)

3. 划分矩形网格

第2步骤中已经知道了大矩形坐标范围,现在就可以根据它们去划分小矩形,在此之前需要指定小矩形的长度,(0.01-0.1之间最佳,单位为KM)。具体代码如下:

import numpy as np


def generate_grids(start_long,start_lat,end_long,end_lat,resolution):
    """
    根据起始的经纬度和分辨率,生成需要需要的网格.
    方向为左上,右下,所以resolution应为 负数,否则未空
    :param start_long:
    :param start_lat:
    :param end_long:
    :param end_lat:
    :param resolution:  划分的网格长度,单位为KM
    :return:
    """
    assert start_long < end_long,'需要从左上到右下设置经度,start的经度应小于end的经度'
    assert start_lat > end_lat,'需要从左上到右下设置纬度,start的纬度应大于end的纬度'
    assert resolution>0,'resolution应大于0'


    grids_lib=[]
    longs = np.arange(start_long,end_long,resolution)
    if longs[-1] != end_long:
        longs = np.append(longs,end_long)

    lats = np.arange(start_lat,end_lat,-resolution)
    if lats[-1] != end_lat:
        lats = np.append(lats,end_lat)
    for i in range(len(longs)-1):
        for j in range(len(lats)-1):
            grids_lib.append([round(float(longs[i]),6),round(float(lats[j]),6),round(float(longs[i+1]),6),round(float(lats[j+1]),6)])
            #yield [round(float(longs[i]),6),round(float(lats[j]),6),round(float(longs[i+1]),6),round(float(lats[j+1]),6)]
    return grids_lib



grids_lib = generate_grids(112.958507, 23.932988, 114.059957, 22.51436,0.1)

最终获取到的grids_lib就是划分出的所有网格范围坐标,数据结构如下:

[[112.975216, 23.463609], [112.977106, 23.463297]]

4. 调用高德API爬取数据

剩下的逻辑就和关键字爬取一样,只是不需要指定city参数,而是替换为polygon参数,参数示例:

112.975216, 23.463609|112.977106, 23.463297

分别是矩形的左上和右下点坐标。

5. 解析数据并写入EXCEL

在这个过程中,需要注意的是矩形的范围肯定是比一个城市范围大的,因此难免爬到的数据会出现所属城市不是所需要的城市的情况,此情况的处理办法是:根据POI数据的adcode和城市的adcode判断,前四位分别代表了省和城市,因此如果前四位编码相同,则是属于同一个城市的数据。

代码地址

完整代码参见:https://github.com/liujiao111/poi

里面的 poi-pology 文件夹 需要改动的在app.py文件 中上部分用TODO样式标出来了。

原创文章,转载请注明出处:http://www.loveyuanwei.com/article/poi-pology/