本文转载自微信公众号「可以叫我才哥」,作者才哥。转载本文请联系可以叫我才哥公众号。
大家好,我是才哥。
最近被催更了,害~
今天我们开启一个系列吧,关于城市道路的,本篇主要演示获取城市道路数据,接下来我们会在此基础上拓展1-2篇好玩的案例,敬请期待!
好了,我们开始今天的案例介绍。
1. 需求分析
我们以北京为例,希望获取该城市全部道路名称信息,主要字段有道路id、道路名称及所在区,基于高德地图的api接口。
我们找到高德api文档:https://lbs.amap.com/api/webservice/guide/api/search#t8
在搜索POI部分发现了查询城市道路名称的关键字搜索接口如下,但是该接口最多只能返回1000个数据。很明显北京市不止1000条道路,那么如何获取全部道路呢?
关键字搜索
终于,我们发现多边形搜索的接口,它可以对指定的矩形区域内的道路进行搜索,这样我们就脑洞一个想法将北京市按照经纬度分割为若干小区域,如何搜索各个区域内的道路数据后汇总就可以了,为了尽可能不要有遗漏,我们可以将区域颗粒度划分的小一些。
多边形搜索
那么,新的问题也来了:如何进行经纬度区域划分呢?我们又找到了行政区域查询接口文档:https://lbs.amap.com/api/webservice/guide/api/district
该接口通过行政区名称关键字就可以返回该行政区域的边界经纬度,如何我们只需要取经纬度各自的最大最小值就可以得到北京市所在的矩形区域,接着对这个矩形区域进行细化即可。
行政区域查询
思路有了,我们就开始干活吧!
2. 获取行政区域边界数据
直接按照开发者文档的案例演示编写代码如下:
- import requests
- import pandas as pd
- import os
- url = 'https://restapi.amap.com/v3/config/district?'
- key = '你的key' # 自己在高德开放平台注册一个即可
- keywords = '北京' # 可以换成你所在的城市
- params = {
- 'key':key,
- 'keywords':keywords,
- 'subdistrict':0,
- 'extensions':'all',
- }
- r = requests.get(url,params=params)
- data = r.json()
- polyline = data['districts'][0]['polyline']
- polyline_list = polyline.split(';')
- df = pd.DataFrame(polyline_list,columns=['经纬度'])
- df[['经度','纬度']] = df['经纬度'].str.split(',',n=1,expand=True).astype(float)
- # 获取区域边界经纬度
- latitude_max = df['经度'].max()
- latitude_min = df['经度'].min()
- longitude_max = df['纬度'].max()
- longitude_min = df['纬度'].min()
最后,矩形区域的四个点的经纬度如下:
- 左上角:115.423411,41.060816
- 右上角:117.514625,41.060816
- 左下角:115.423411,39.442758
- 右下角:117.514625,39.442758
矩形区域
上图中我们可以看到矩形区域很多部分不属于北京,所以在后续的道具数据采集的时候需要进行判断道具归属省份是否为北京。
3. 将行政区域分块
既然我们得到了北京所属矩形区域的边界点经纬度,那么直接这个矩形区域进行网格化就行了,处理过程比较简单,直接看代码:
- # 绘制网格,这里按照20*20共400个网格
- def get_polygons(latitude_num,longitude_num):
- # latitude_num = 20
- # longitude_num = 20
- latitude_step = (latitude_max - latitude_min)/latitude_num
- longitude_step = (longitude_max - longitude_min)/longitude_num
- polygons = []
- for i in range(latitude_num):
- latitude_leftup = latitude_min + latitude_step * i
- latitude_rightdown = latitude_min + latitude_step * (i+1)
- for j in range(longitude_num):
- longitude_leftup = longitude_max - longitude_step * j
- longitude_rightdown = longitude_max - longitude_step * (j+1)
- polygon = f'{latitude_leftup},{longitude_leftup}|{latitude_rightdown},{longitude_rightdown}'
- polygons.append(polygon)
- return polygons
我们得到了用于区域搜索经纬度坐标对如下:
- # polygons
- ['115.423411,41.060816|115.5279717,40.979913100000005',
- '115.423411,40.979913100000005|115.5279717,40.8990102',
- '115.423411,40.8990102|115.5279717,40.8181073',
- '115.423411,40.8181073|115.5279717,40.7372044',
- ...
- ]
网格化
4. 获取道路数据
到这一步,我们只需要遍历全部的坐标对polygons,然后搜索该区域内满足归属省份为北京市的全部道路即可。
- # 获取指定区域指定page的道路数据并存到本地
- def get_road(polygon,page):
- url = 'https://restapi.amap.com/v3/place/polygon?'
- params = {
- 'key':key,
- 'polygon':polygon,
- 'keywords':'道路名',
- 'types':190301,
- 'offset':20,
- 'page':page,
- 'extensions':'all',
- }
- r = requests.get(url,params=params)
- data = r.json()
- pois = data['pois']
- file_name = '北京道路名称数据.csv'
- for poi in pois:
- if poi['pname'] =='北京市':
- df = pd.DataFrame({
- 'road_id' : poi['id'],
- 'road_name' : poi['name'],
- 'road_adname' : poi['adname']
- },index=[0])
- if os.path.exists(file_name):
- df.to_csv(file_name, mode='a', header=False,
- index=None, encoding='utf_8_sig')
- else:
- df.to_csv(file_name, index=None, encoding='utf_8_sig')
- return pois
- # 这里分为20*20共400个区域
- polygons = get_polygons(20,20)
- for i,polygon in enumerate(polygons):
- page = 1
- while True:
- pois = get_road(polygon, page)
- if pois == []:
- break
- page += 1
- print(f'\r正在爬取第{i+1}/400个区域的道路数据',end='')
最终,我们得到了北京一共有14994条道路,其中各区道路数分别如下:
区 | 道路数 |
---|---|
顺义区 | 2164 |
大兴区 | 1826 |
通州区 | 1310 |
朝阳区 | 1264 |
海淀区 | 1088 |
房山区 | 912 |
密云区 | 907 |
西城区 | 896 |
东城区 | 818 |
昌平区 | 801 |
平谷区 | 770 |
丰台区 | 673 |
延庆区 | 553 |
门头沟区 | 378 |
怀柔区 | 372 |
石景山区 | 262 |
总计 | 14994 |