轻松获取城市道路数据 | 用Python发现北京一共有1.5万条道路!
大家好,我是小五🧐
今天分享一篇文章,主要演示如何获取城市道路数据。
好了,我们开始今天的案例介绍。
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 |
以上就是本次全部内容,感兴趣的同学在下方公众号【快学Python】(非本号)中回复道路
可以获得源码。
后续我们结合道具数据试试一些好玩的探索吧,或者你有什么想法可以留言交流哦!