想知道所在的城市有多少條道路?我用Python發現北京一共有1.5萬條道路!
本文轉載自微信公眾號「可以叫我才哥」,作者才哥。轉載本文請聯系可以叫我才哥公眾號。
大家好,我是才哥。
最近被催更了,害~
今天我們開啟一個系列吧,關于城市道路的,本篇主要演示獲取城市道路數據,接下來我們會在此基礎上拓展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 |