原文地址:http://www.mapboxx.cn/article/poi-pology/
高德地图提供了三种方式来获取POI数据:
根据关键字进行搜索指定城市内的POI数据,比如爬取广州市的大学;爬取某个中心点一定半径范围内的数据;爬取某个多边形范围内的数据。根据关键字搜索的可以参见目前实现的工具:http://www.mapboxx.cn/tool/poiview/ 本次打算实现第三种,根据多边形范围爬取POI数据。大概思路如下:
获取需要爬取的城市边界坐标数据,并计算出最大经度、纬度和最小经度、纬度(为了把城市包括在一个大矩形范围内);根据一定的距离将大矩形划分为N多个小矩形范围,并获取其小矩形的边界坐标;调用高德地图POI,传入小矩形的边界坐标以及需要爬取的POI类型,即可得到所需数据申请高德开放平台账号,并且申请开发者认证,然后申请web服务类型的密钥。
使用的高德行政区划查询接口,地址: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)第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]]剩下的逻辑就和关键字爬取一样,只是不需要指定city参数,而是替换为polygon参数,参数示例:
112.975216, 23.463609|112.977106, 23.463297分别是矩形的左上和右下点坐标。
在这个过程中,需要注意的是矩形的范围肯定是比一个城市范围大的,因此难免爬到的数据会出现所属城市不是所需要的城市的情况,此情况的处理办法是:根据POI数据的adcode和城市的adcode判断,前四位分别代表了省和城市,因此如果前四位编码相同,则是属于同一个城市的数据。
完整代码参见:https://github.com/liujiao111/poi
里面的 poi-pology 文件夹 需要改动的在app.py文件 中上部分用TODO样式标出来了。