我们研究生的课程内容,做下笔记记录一下。
使用的python环境是python3.7
用的图大部分都是老师ppt里的图,懒得自己截了……
申请百度开发者密匙
(1)注册百度用户,注册过的话,直接登录就可以。登录地址为百度地图开放平台
(2)登录后,在控制台点击【创建应用】。
(3)填写表单,创建应用
(4)这样就得到了API Key了
百度地图搜索API语法
poi查询的基本url为http://api.map.baidu.com/place/v2/search?
按矩形框坐标范围检索
有如下参数
参数名 | 参数含义 | 类型 | 是否必须 |
---|---|---|---|
query | 检索关键字,周边检索和矩形区域检索 支持多关键字(以$隔开)并集检索,最多支持10个。 |
string(45) | Y |
bounds | 设置查询的坐标范围 矩形框的左下角经纬度和右上角经纬度。 |
string(50) | Y |
output | 输出格式为json或xml | string(50) | Y |
scope | 检索结果的详细程度 取值为1或空,返回基本信息;取值2,返回详细信息 |
string(50) | N |
page_size | 页面显示POI数量,默认值为10条,最大值为20 | int | N |
page_num | 分页页码,从0开始 | int | N |
coord_type | 坐标类型 1:WGS84即GPS经纬度坐标 2:国家测绘局GCJ-02坐标 3:bd09,即百度经纬度坐标 4:bd09mc即百度米坐标 |
int | N |
ret_coordtype | 返回国测局经纬度坐标 | string(50) | N |
ak | 开发者访问密钥(刚刚申请的API KEY) | string(50) | Y |
关于上面这个bounds的值,可以使用百度地图自己提供的坐标拾取器获得。也可以通过别人写的一个网页获得具体的行政区矩形框。
别人写的网页的html如下
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";} #panel{ position:absolute; left:5px; top:5px; } #result{ background: #fff; padding:5px; } </style> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ"></script> <title>添加行政区划</title> </head> <body> <div id="allmap"></div> <div id="panel"> <div> <input type="text" id="keyword" value="昆明市"/> <input type="button" value="查看范围" id="commitBtn"/> 边界经纬度坐标 <textarea id="pathStr"></textarea> 边界墨卡托坐标 <textarea id="pathMc"></textarea> </div> <div id="result"> </div> </div> </body> </html> <script type="text/javascript"> // 百度地图API功能 var map = new BMap.Map("allmap"); map.centerAndZoom(new BMap.Point(116.403765, 39.914850), 5); map.enableScrollWheelZoom(); var mercatorProjection = map.getMapType().getProjection(); $("#commitBtn").bind('click', function(){ getBoundary($("#keyword").val()); }); function getBoundary(city){ var bdary = new BMap.Boundary(); bdary.get(city, function(rs){ //获取行政区域 map.clearOverlays(); //清除地图覆盖物 var count = rs.boundaries.length; //行政区域的点有多少个 if (count === 0) { alert('未能获取当前输入行政区域'); return ; } var pointArray = []; for (var i = 0; i < count; i++) { var ply = new BMap.Polygon(rs.boundaries[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物 map.addOverlay(ply); //添加覆盖物 pointArray = pointArray.concat(ply.getPath()); } var pathStr = ""; var pathMc = ""; for (var i = 0; i < pointArray.length; i++) { var mc = mercatorProjection.lngLatToPoint(pointArray[i]); pathStr += pointArray[i].lng + "," + pointArray[i].lat + ";"; pathMc += mc.x + "," + mc.y + ";"; } $('#pathStr').html(pathStr); $('#pathMc').html(pathMc); var ply = new BMap.Polygon(pointArray , {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物 var bounds = ply.getBounds(); var ne = bounds.getNorthEast(); var sw = bounds.getSouthWest(); var neMc = mercatorProjection.lngLatToPoint(ne); var swMc = mercatorProjection.lngLatToPoint(sw); var str = "经纬度:左下角,右上角:" + sw.lng + "," + sw.lat + ";" + ne.lng + "," + ne.lat + "<br/>墨卡托坐标:左下角,右上角:" + swMc.x + "," + swMc.y + ";" + neMc.x + "," + neMc.y; $('#result').html(str); console.log(bounds); map.setViewport(pointArray); //调整视野 }); } //getBoundary('北京'); </script>
打开后,类似这样
按城市检索
刚刚说的是按bound检索。也可以使用region参数按城市检索,比如说
http://api.map.baidu.com/place/v2/search?query=公园®ion=武昌区&output=json&ak=xxxxxxxxx&page_size=20&page_num=0
周边检索
使用location参数(设置中心点坐标)和radius参数(设置半径)进行周边检索。比如说
http://api.map.baidu.com/place/v2/search?query=酒店&location=30.531642,114.366409&radius=300&output=json&ak=xxxxxxxxxx&page_size=20&page_num=0
返回内容
返回的内容,status为状态(0为成功返回,其他为异常),message为提示信息,total为返回的poi数量,results为当前页的poi信息。
{ "status":0, "message":"ok", "total":57, "results":[] }
一般scope为1的poi信息如下
{ "name":"四美塘", "location":{ "lat":30.603315, "lng":114.344284 }, "address":"武汉市武昌区和平大道589号(长江二桥下)", "province":"湖北省", "city":"武汉市", "area":"武昌区", "street_id":"15a93810075519bd15c8f6b4", "detail":1, "uid":"f21f49135aad2a3ce856fad9" }
一般scope为2的poi信息如下
{ "name":"四美塘", "location":{ "lat":30.603315, "lng":114.344284 }, "address":"武汉市武昌区和平大道589号(长江二桥下)", "province":"湖北省", "city":"武汉市", "area":"武昌区", "street_id":"15a93810075519bd15c8f6b4", "detail":1, "uid":"f21f49135aad2a3ce856fad9" "detail_info":{ "tag":"旅游景点;公园", "navi_location":{ "lng":114.34584347395, "lat":30.604976470638 }, "type":"scope", "detail_url":"http://api.map.baidu.com/place/detail?uid=f21f49135aad2a3ce856fad9&output=html&source=placeapi_v2", "overall_rating":"4.3", "comment_num":"14", "children":[ ] } }
百度地图POI搜索爬虫设计
由于百度地图限制每次返回的poi最多只有400个,所以我们要把矩形区域进行分割以获得更多的数据。
具体实现的代码如下
def splitArea(bound): # 分割矩形区域的函数 boundList=[] row_num = 2 # 按照 2 X 2 进行分割 bound=list(map(float,bound.split(','))) step_lat = (bound[2] - bound[0]) / row_num step_lon = (bound[3] - bound[1]) / row_num for i in range(0,row_num): for j in range(0,row_num): boundTemp = [] boundTemp.append(bound[0]+step_lat*i) boundTemp.append(bound[1]+step_lon*j) boundTemp.append(bound[0]+step_lat*(i+1)) boundTemp.append(bound[1]+step_lon*(j+1)) boundTemp=",".join(["%s" %x for x in boundTemp]) boundList.append(boundTemp) return boundList
课后作业完成
题目如下:
寻找武汉市中学(或小学)周围500米(或其他)内的网吧
要求:提交代码py文件及运行结果文件(txt),txt文件格式如下:
1,XXX小学
1-1,XXX网吧
1-2, XXX网吧
2,XXX小学
2-1,XXX网吧
2-2, XXX网吧
………..
写的python3代码为
# coding:utf-8 # version:python3.7 # author:Ivy ############# 程序功能 #################### # 本程序用来获取百度地图某城市某种poi周围一定范围内的另一种类型的poi数据 # 如示例是获取武汉市中学周围500米内的网吧 # 生成的info.txt格式如下 # 1, xxx中学 # 1-1, xxx网吧 # 1-2, xxx网吧 # 2, xxx中学 # 2-1, xxx网吧 ############################################ import requests,json import time,sys ############### 自主设置区 ############### ak = 'xxxxxxxxx' #API key keyword="中学" keyword2="网吧" radius=500 city="武汉市" baseBound = '29.972898,113.707695,31.367052,115.085775' #武汉市矩形框的左下角经纬度和右上角经纬度 ############################################ # 构造header headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36', 'Accept-Encoding':'gzip, deflate', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 'Host': 'api.map.baidu.com', 'Upgrade-Insecure-Requests': '1', 'Connection': 'keep-alive', 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 'DNT': '1' } baseUrl="http://api.map.baidu.com/place/v2/search?query={}&bounds={}&page_size=20&page_num={}&output=json&ak={}" searchBaseUrl="http://api.map.baidu.com/place/v2/search?query={}&location={}&radius={}&output=json&ak={}&page_size=20&page_num={}" def req(url): # 访问url获取返回的数据读取为json errorNum=0 while True: try: res = requests.get(url,headers=headers) res.encoding='utf-8' jd = json.loads(res.text) return jd except Exception as e: errorNum+=1 print("出错了!") print(e) if errorNum==5: sys.exit(1) time.sleep(1) def splitArea(bound): # 分割矩形区域的函数 boundList=[] row_num = 2 # 按照 2 X 2 进行分割 bound=list(map(float,bound.split(','))) step_lat = (bound[2] - bound[0]) / row_num step_lon = (bound[3] - bound[1]) / row_num for i in range(0,row_num): for j in range(0,row_num): boundTemp = [] boundTemp.append(bound[0]+step_lat*i) boundTemp.append(bound[1]+step_lon*j) boundTemp.append(bound[0]+step_lat*(i+1)) boundTemp.append(bound[1]+step_lon*(j+1)) boundTemp=",".join(["%s" %x for x in boundTemp]) # 是否要继续分割 if check(boundTemp): boundList.append(boundTemp) else: boundList+=splitArea(boundTemp) return boundList def check(bound): # 检查一下当前矩形框是否需要再次分割 checkUrl=baseUrl.format(keyword,bound,0,ak) checkJd=req(checkUrl) if checkJd["total"]<400: return True return False def getPois(bound): # 获取一定范围内所有的想要的poi的信息 poiListTemp=[] pageNum=0 while True: getUrl=baseUrl.format(keyword,bound,pageNum,ak) getJd=req(getUrl) if getJd['results']==[]: # 如果这一页没数据了就是结束啦,退出循环 break for result in getJd['results']: if result["city"] != city: # 在矩形框中,但是不是想要的城市的,就不要记录啦 continue poiListTemp.append(result) pageNum+=1 print("当前区域{}的poi已爬完,共有{}个".format(bound,len(poiListTemp))) return poiListTemp def searchPois(poiInfo): # 获取某个poi周边的其他poi信息 poiListTemp=[] pageNum=0 while True: searchUrl=searchBaseUrl.format(keyword2,str(poiInfo['location']['lat'])+','+str(poiInfo['location']['lng']),radius,ak,pageNum) searchJd=req(searchUrl) if searchJd['results']==[]: break for result in searchJd['results']: poiListTemp.append(result["name"]) print(result["name"]," 已添加") pageNum+=1 print(poiInfo["name"],"周边poi已爬完") return [poiInfo["name"],poiListTemp] if __name__ == '__main__': # 分割想要的区域 boundList=splitArea(baseBound) print(boundList) # 获取范围内所有的poi信息 poiList=[] for bound in boundList: poiList+=getPois(bound) print('已获得所有poi信息') # 对于获取到的每个poi进行周边检索 infoList=[] for poi in poiList: infoList.append(searchPois(poi)) print('已获得所有周边poi信息') # 把获取到的信息都写入txt with open('info.txt','w') as f: for i in range(len(infoList)): f.write(str(i+1)+', '+infoList[i][0]+'\n') for j in range(len(infoList[i][1])): f.write(str(i+1)+'-'+str(j+1)+', '+infoList[i][1][j]+'\n') print("所获得的信息已全部写入txt啦!")
【参考】
[1]我们老师的PPT(不便公开)