小伙伴们大家好,在上一期的推文中我们介绍了如何利用百度地图的API获取POI兴趣点的相关信息,详见:
干货 | 10分钟教你用Python获取百度地图各点的经纬度信息
但是只是简单介绍了API的调用方式。今天我们来讲讲如何在Python里面调用申请的API接口,然后利用Python进行相关的数据处理,最终得到我们想要的信息。
最近大家还是要响应号召,不出门!在家好好学习吧~
地点检索方式
目前百度地图的地点检索服务有以下4种方式:
行政区划区域检索:开发者可通过该功能,检索某一行政区划内(目前最细到城市级别)的地点信息。
圆形区域检索:开发者可设置圆心和半径,检索圆形区域内的地点信息(常用于周边检索场景)。
矩形区域检索:开发者可设置检索区域左下角和右上角坐标,检索坐标对应矩形内的地点信息(常用于手机或PC端地图视野内检索)
地点详情检索:不同于以上三种检索功能。地点详情检索针对指定POI,检索其相关的详情信息。开发者可以通过三种区域检索(或其他服务)功能,获取POI id。使用“地点详情检索”功能,传入id,即可检索POI详情信息,如评分、营业时间等(不同类型POI对应不同类别详情数据)。
常用的方式主要是第一种和第二种,今天对这两种方式都介绍一下。
行政区划区域检索
上次说了,API的调用方式是通过编辑好的URL,请求服务器然后返回所需要的的数据,数据是JSON或者XML类型的(别问我什么是JSON)。
具体的说明大家去官网看吧balablaba的……这里我就不在BB了,直接贴上一个编辑好的URL:
http://api.map.baidu.com/place/v2/search?query=超市®ion=武汉市&output=json&ak=申请的AK&scope=1&page_size=20&page_num=0
上面URL中,绿色标出的是需要填写的参数。各个参数的说明如下:
query | 检索关键字。行政区划区域检索不支持多关键字检索。如果需要按POI分类进行检索,请将分类通过query参数进行设置,如query=美食。 |
region | 检索行政区划区域(增加区域内数据召回权重,如需严格限制召回数据在区域内,请搭配使用city_limit参数),可输入行政区划名或对应cityCode。 |
outpu | 出格式为json或者xml。 |
ak | 开发者的访问密钥,必填项。v2之前该属性为key。 |
scope | 检索结果详细程度。取值为1 或空,则返回基本信息;取值为2,返回检索POI详细信息。 |
page_size | 单次召回POI数量,默认为10条记录,最大返回20条。多关键字检索时,返回的记录数为关键字个数*page_size。 |
page_num | 分页页码,默认为0,0代表第一页,1代表第二页,以此类推。常与page_size搭配使用。 |
关于其他可选参数更多详细信息请戳:
http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi
值得注意的是,page_size=20&page_num=0表示每个URL页面返回的POI数量为20个,这个是第0个页面,因为在程序中,一般都是从0开始的。
好了,请求讲完了,接下来放Python代码:
#coding: utf-8 import requests import json import time import csv import codecs """ 查询关键字: """ FileKey = 'preclass' KeyWord = u"超市" def getBaiduApiAk(): """ 获取配置文件中百度apikey: { "baiduak":"xx"} :return: str """ return "填写你申请的AK" def requestBaiduApi(keyWords, baiduAk, fileKey): today = time.strftime("%Y-%m-%d") pageNum = 0 count = 0 logfile = open("./" + fileKey + "-" + today + ".log", 'a+', encoding='utf-8') file = open("./" + fileKey + "-" + today + ".txt", 'a+', encoding='utf-8') file_csv = open('shops_data_青山区.csv', 'w+', encoding='utf-8') # 追加 writer = csv.writer(file_csv) writer.writerow(["no","area","name","lat","lng"]) # print('-------------') # print(index) while True: try: URL = "http://api.map.baidu.com/place/v2/search?query=" + keyWords + \ "®ion=" + "武汉市青山区" + \ "&output=json" + \ "&ak=" + baiduAk + \ "&scope=1" + \ "&page_size=20" + \ "&page_num=" + str(pageNum) # print(pageNum) print(URL) resp = requests.get(URL) res = json.loads(resp.text) # print(resp.text.strip()) if len(res['results']) == 0: logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " stop " + " " + str(pageNum) + '\n') break else: for r in res['results']: # print(r) count += 1 city_area = r['city']+r['area'] shop_name = r['name'] shop_lat = str(r['location']['lat']) shop_lng = str(r['location']['lng']) writer.writerow([str(count),city_area, shop_name, shop_lat, shop_lng]) # file.writelines(str(r).strip() + '\n') # print(r['city']+r['area']+" "+r['name']+" "+str(r['location']['lat']) + " " + str(r['location']['lng'])) pageNum += 1 time.sleep(1) except: print("except") logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " except " + " " + str(pageNum) + '\n') break def main(): baiduAk = getBaiduApiAk() requestBaiduApi(keyWords=KeyWord, baiduAk=baiduAk, fileKey=FileKey) if __name__ == '__main__': main()
(代码于2020.1.18测试无误)
代码的思路也相当简单,首先是构造URL,然后请求返回JSON格式的数据,Python处理后写入CSV文件中。
获取的数据详情如下所示,有需要的同学可根据说明修改相应的参数获取相应的数据:
值得注意的是,在实际请求中,百度API限制了检索只能返回20个URL页面。这就意味着我们一个区域最多只能检索20*20=400个POI点。实际需求中往往不止400个点的。
但人民的智慧是无穷的,我们接下来介绍第二种方式解决上面400个点的弊端。
矩形区域检索
所谓矩形区域检索,就是给定一个矩形范围的经纬度坐标(实际上两点即可定位一个矩形,左下点和右上点),然后在该矩形范围内进行兴趣点的检索。
而矩形范围经纬度坐标的确定可以利用之前介绍的坐标拾取系统进行拾取。
例如下图在武汉市(武汉加油!)拾取一个蓝色框的区域,把左下角的经纬度和右上角的经纬度记录下来即可,这样一个范围就做好啦。
例如我们拾取了一个矩形:
左下点的经纬度为:114.2540523, 30.471019
右上点的经纬度为:114.2687126, 30.4877379
现在利用矩形区域检索的URL如下:
http://api.map.baidu.com/place/v2/search?query=超市&bounds=30.471019,114.2540523,30.4877379,114.2687126&output=json&ak=申请的AK&scope=1&page_size=20&page_num=0
参数无太大变化,就是region变成了bounds,并且指出了矩形区域的边界经纬度(左下点和右上点)。注意绿色处要填上你自己的AK。
好了,现在检查一下URL编辑是否正确,复制到浏览器回车一下看看:
OK,大功告成。好了,现在我们来解决400个点限制的问题。不知道聪明的你们想到了没有。
没错,就是切割区域。既然百度限制了每个区域检索最多只能返回400个点,那么可以通过矩形检索的方式,将一个大矩形切割成很多小矩形,依次在每个小矩形内进行检索,最后将所有小矩形的结果加起来就有很多很多个点啦。怎样,是不是很聪明呢!
例如将一个大区域分割成1234号区域分别检索,假如每个区域都返回400个点,那么总共就能获取4X400=1600个点了。
而如何分割,则不必手动拾取点进行划分,可以利用程序来计算嘛!
好了,下面给出一份分割区域的Python代码:
#coding: utf-8 import requests import json import time import csv """ 查询关键字: """ FileKey = 'preclass' KeyWord = u"便利店" """ 关注区域的左下角和右上角百度地图坐标(经纬度) """ BigRect = { 'left': { 'x': 114.239392, 'y': 30.471019 }, 'right': { 'x': 114.385995, 'y': 30.638208 } } """ 定义细分窗口的数量,横向X * 纵向Y """ WindowSize = { 'xNum': 10.0, 'yNum': 10.0 } """ 获取AK :return: str """ def getBaiduApiAk(): return "申请的AK" def getSmallRect(bigRect, windowSize, windowIndex): """ 获取小矩形的左上角和右下角坐标字符串(百度坐标系) :param bigRect: 关注区域坐标信息 :param windowSize: 细分窗口数量信息 :param windowIndex: Z型扫描的小矩形索引号 :return: lat,lng,lat,lng """ offset_x = (bigRect['right']['x'] - bigRect['left']['x'])/windowSize['xNum'] offset_y = (bigRect['right']['y'] - bigRect['left']['y'])/windowSize['yNum'] left_x = bigRect['left']['x'] + offset_x * (windowIndex % windowSize['xNum']) left_y = bigRect['left']['y'] + offset_y * (windowIndex // windowSize['yNum']) right_x = (left_x + offset_x) right_y = (left_y + offset_y) return str(left_y) + ',' + str(left_x) + ',' + str(right_y) + ',' + str(right_x) def requestBaiduApi(keyWords, smallRect, baiduAk, index, fileKey, count): today = time.strftime("%Y-%m-%d") pageNum = 0 logfile = open("./" + fileKey + "-" + today + ".log", 'a+', encoding='utf-8') file_csv = open('testdata.csv', 'w+', encoding='utf-8',newline='') # 追加 writer = csv.writer(file_csv) writer.writerow(["no","area","name","lat","lng"]) # print('-------------') # print(index) while True: try: URL = "http://api.map.baidu.com/place/v2/search?query=" + keyWords + \ "&bounds=" + smallRect + \ "&output=json" + \ "&ak=" + baiduAk + \ "&scope=1" + \ "&page_size=20" + \ "&page_num=" + str(pageNum) print(pageNum) print(URL) resp = requests.get(URL) res = json.loads(resp.text) # print(resp.text.strip()) if len(res['results']) == 0: logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " stop " + str(index) + " " + smallRect + " " + str(pageNum) + '\n') break else: for r in res['results']: # print(r) count += 1 city_area = r['city'] + r['area'] shop_name = r['name'] shop_lat = str(r['location']['lat']) shop_lng = str(r['location']['lng']) writer.writerow([str(count), city_area, shop_name, shop_lat, shop_lng]) pageNum += 1 time.sleep(1) except: print("except") logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " except " + str(index) + " " + smallRect + " " + str(pageNum) + '\n') break return count def main(): baiduAk = getBaiduApiAk() count = 0 for index in range(int(WindowSize['xNum'] * WindowSize['yNum'])): smallRect = getSmallRect(BigRect, WindowSize, index) count = requestBaiduApi(keyWords=KeyWord, smallRect=smallRect, baiduAk=baiduAk, index=index, fileKey=FileKey,count= count) time.sleep(1) print(str(count)) if __name__ == '__main__': main()
(代码于2020.1.18测试无误)
读者只需要简单修改代码中的:
- 查询关键字
- 关注区域的左下角和右上角百度地图坐标(经纬度)
- 定义细分窗口的数量,横向X * 纵向Y
- 获取AK
这几处的相关信息即可使用,生成的数据如下所示:
怎样,是不是很简单呢!至此,两种方式已经介绍完毕。当然,获取经纬度信息只是我们的第一步操作,后续的过程我们将向大家展示如何根据经纬度信息获取两点之间的真实距离。
希望这次的疫情尽快过去,大家都相安无事!
小编还要回去上学吃热干面呢!武汉加油!