一文教会你如何基于高德地图获取城市公交网络数据(包含经纬度) 您所在的位置:网站首页 地铁站系统程序语言 一文教会你如何基于高德地图获取城市公交网络数据(包含经纬度)

一文教会你如何基于高德地图获取城市公交网络数据(包含经纬度)

2023-04-20 11:12| 来源: 网络整理| 查看: 265

之前写过1篇如何基于8684公交查询网站获取城市公交网络数据的文章,但获取到的公交网络数据里是不包含经纬度信息的。

近日,有小伙伴咨询我,怎样把公交线路和各站点的经纬度信息也同步获取到,从而进行一些可视化展示。

周末闲来无事,就基于高德地图简单研究了一下,并把实现过程分享出来。

1 获取公交线路名称集合

想要获取整个城市的公交线路数据,第一步是先拿到城市公交线路的名称集合,然后再基于线路名去请求各个线路的详细信息。

这一过程可以基于8684公交查询网站来实现,具体过程可参见我之前的博文。

在这里插入图片描述 代码如下:

def get_busline_names(city_name): request_url = "https://%s.8684.cn" %(city_name) init_href = "/list1" start_html = requests.get(request_url + init_href) soup = BeautifulSoup(start_html.text, 'lxml') #以lxml的方式解析html文档 pre_list = soup.find('div', class_='tooltip-body cc-content-tooltip').find('div', class_='tooltip-inner').find_all('a') busline_names = list() for pre in pre_list: # 拼接出pre前缀公交线路的请求url url = request_url + pre['href'] pre_html = requests.get(url) pre_soup = BeautifulSoup(pre_html.text, 'lxml') busline_names_a = pre_soup.find('div', class_='list clearfix').find_all('a') for busline_name_a in busline_names_a: busline_names.append(busline_name_a.get_text()) return busline_names 2 获取各个公交线路详情

8684公交查询网站是不包含公交网络的经纬度数据的,要获取经纬度数据,只能借助于地图应用来获取,比如高德地图。

获取高德地图数据,基本有3种途径:

直接调用高德地图开放出来的Web API

简单易用,可以快速上手。但该途径需要注册高德地图账号,并申请AK,且有日调用次数的限制。

同时,可能某些数据未提供相应接口。

高德地图的JS API

JS API Demo示例里提供了很多数据接口,可以通过浏览器开发者工具抓包分析出后台的数据http服务接口,并得到请求的构建方法,然后模拟发送请求。

直接分析高德地图的检索请求,直接调用相关服务接口

该种方法需要在高德地图上构建好检索请求,通过浏览器开发者工具抓包分析出后台的数据http服务接口,并得到请求的构建方法,然后模拟发送请求。

首先看一下高德地图的Web API页面:

在这里插入图片描述 发现高德地图并没有提供公交线路检索的Web API。

有个POI检索倒是可以试试,但尝试了一下之后,发现也不能满足我们的需求,比如关键字给"1路",其返回的均是1路公交线上各个站点的信息,并没有线路信息。可以看出,POI Web API关注的更多是点信息。

接着尝试第2种途径:

在这里插入图片描述 可以看到,JS API里提供了公交线路检索的接口。

下面就是抓包分析请求了,老规矩,打开浏览器的开发者工具:

在这里插入图片描述 很容易就找到了目标接口,同时观察接口返回数据,其中的polyline字段就是线路的经纬度集合,而busstops字段是该线路各个站点的信息,其中也包含了站点的经纬度信息。

接着看一下该接口的请求信息(参数、headers等):

在这里插入图片描述 一般来讲,参数和headers里面的大部分参数均是可选参数,我们可以尝试去掉某些参数,然后观察请求是否仍然可以正常返回。

逐步分析出该请求的headers必须包含下述两项:

headers = { 'Host': 'restapi.amap.com', 'Referer': 'https://lbs.amap.com/' }

请求参数如下:

params = { "s": "rsv3", "key": "******", "city": "******", "pageIndex": "1", "offset": "20", "extensions": "all", "keywords": "1路" }

city参数代表城市名称或者citycode,offset代表每次请求单页包含的元素数,extensions配置必须为all的时候才会有经纬度数据,keywords配置传入公交线路名称。

不应该传入1条公交线路名称,只返回1个结果吗,为啥还要配置offset呢?

首先是因为,同一个公交线路名称一般会有正、反2个方向,同时,高德地图在检索的时候,采用的是模糊搜索策略,比如你搜索"1路",“11路”、"121路"等可能也会在返回集合里,其返回的时候会根据匹配得分进行排序,一般取前2个元素(正好对应线路的2个方向)。

分析完毕,下面代码实现一下:

def get_busline_info_amap(busline_names, citycode, key, col_sink, time_delay): request_url = "https://restapi.amap.com/v3/bus/linename" params = { "s": "rsv3", "key": key, "city": citycode, "pageIndex": "1", "offset": "20", "extensions": "all", "keywords": "1路" } headers = { 'Host': 'restapi.amap.com', 'Referer': 'https://lbs.amap.com/', } # 线路的set集合,保证线路无重复 buslines_loaded = set() count = 0 for line_name in busline_names: params["keywords"] = line_name res = requests.get(url=request_url, headers=headers, params=params).json() if res["status"] == "1": for busline in res["buslines"]: busline_name = busline["name"] # 若该线路未请求过,则创建请求 if busline_name not in buslines_loaded: buslines_loaded.add(busline_name) col_sink.insert_one(busline) count += 1 print("第%d条公交线路%s采集完成!" % (count, busline_name)) # 休眠time_delay秒,防止对高德服务器太高压力 time.sleep(time_delay) # 主函数 if __name__ == '__main__': key = "******" # 接口请求之间的间隔 time_delay = 0.01 city_name = "北京市" city_py = "beijing" db_name = 'web_map' col_name = 'buslines_' + city_py # MongoDB数据库所在的服务器 host = '******' port = 27017 # 获取mongodb的表句柄 col = mongo_util.get_col(db_name, col_name, host, port) busline_names = get_busline_names(city_py) # 获取北京市公交网络数据 get_busline_info_amap(busline_names, city_name, key, col, time_delay)

点击运行,数据哗啦啦入库,任务完成!

在这里插入图片描述 第3种途径的实现方式跟第2种步骤基本一致,区别就是请求的构建方式略有不同,此处不再赘述,读者可自行实践。

需要注意的是: 高德地图返回的经纬度是火星坐标系,在进行可视化展示时,若GIS瓦片地图是WGS-84或者百度坐标系时,需要将坐标转换到对应坐标系,否则会出现线路与底图无法对齐的问题。

本文到此结束,谢谢阅读!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有