Python之爬取百度地图兴趣点(POI)数据

您所在的位置:网站首页 百度坐标拾取器原理 Python之爬取百度地图兴趣点(POI)数据

Python之爬取百度地图兴趣点(POI)数据

2024-06-19 09:50| 来源: 网络整理| 查看: 265

关于爬虫系列,前三篇文章分别讲了三个简单案例,分别爬取了《你好,李焕英》电影豆瓣热门短评、58同城在售楼盘房源信息以及安居客网二手房小区详情页数据。通过前三个案例,相信大家都对爬虫有了简单了解和运用,对于大部分网站来说呢,爬虫的基本流程都相差不多,主要区别在于解析网站标签的xpath语句不同,俗话说得好,熟能生巧,多去尝试爬取不同网站,慢慢地就会熟练使用了。

这篇文章呢,主要来谈一谈如何利用Python调用百度地图API接口,将研究区域看成是一个矩形,以固定经纬度间隔划分网格,爬取百度地图上的兴趣点(Point of interest),获取的字段主要包括名称、纬度、经度、详细地址、省份、市以及区共7个字段。 对于有些知识点,比如百度地图的APK密钥注册等,网上各大博客都有很好的讲解,这里我会放上参考链接,小伙伴们各取所需,本文重点主要放在POI数据爬取的代码上。好了,废话不多说,开始走起~

1. 在百度地图开放平台注册,获取AK密钥

在爬取POI数据之前,必须先在百度地图开放平台上完成注册拿到AK密钥,才能调用百度地图的API接口,关于这部分本文不作过多的详解,具体可以参考如下博客:

参考资料:零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(基础篇)

 2. 获取石家庄市大致范围的经纬度

由于我们将研究范围看作是一个矩形,必然需要矩形的四个夹角经纬度,也就是说只要得到左下角和右上角经纬度,就可以大致知道地图所在位置。那么如何获取到左下角和右上角经纬度呢?在这篇博客零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(进阶篇)中提及到一种方法,代码已经编好,只需将下面代码复制粘贴到txt文件中,然后修改文件后缀名为.html即可。

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; } 添加行政区划 边界经纬度坐标 边界墨卡托坐标 // 百度地图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 + "墨卡托坐标:左下角,右上角:" + swMc.x + "," + swMc.y + ";" + neMc.x + "," + neMc.y; $('#result').html(str); console.log(bounds); map.setViewport(pointArray); //调整视野 }); } //getBoundary('北京');

用本地浏览器打开这个html网页,输入研究的省市即可显示左下角和右上角经纬度,具体操作看下图:

所以我们可以得出:

lat_1 = 37.444122 # 石家庄市左下纬度 lon_1 = 113.529103 # 新石家庄市左下经度 lat_2 = 38.768031 # 石家庄市右上纬度 lon_2 = 115.486183 # 石家庄市右上经度 3. 获取URL URL = "http://api.map.baidu.com/place/v2/search?query=政府& bounds=37.973948,114.382523,38.17371,114.714494&page_size=20&page_num=0&output=json&ak=xxxxx"

URL的各参数含义:① query:表示检索的POI类型;② bounds:采用矩形分割,依次分别为小网格的左下角纬度、左下角经度、右上角纬度、右上角经度;③ page_size:每页所能获取的最大记录数;④ page_num:页数;⑤ output:返回的是json数据;⑥ ak:从百度地图注册平台上申请到的密钥;

具体的参数详解,可以看下这篇博客:零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(代码篇)

4. 爬取百度地图POI数据——代码详解

百度地图给出了两种获取POI的方式,一种是按照行政区域搜索,另一种是按照矩形框搜索;

第一种,按行政区划检索POI,受到百度地图api的限制,最多开源读取20页数据,共计400条,所以这种方法比较适用于数量较小的POI爬取,不推荐在大的工程项目中使用。

第二种,按矩形框搜索,通过给定矩形框的左下角和右上角经纬度坐标,结合API进行爬取,爬取所需时间受两方面限制,一种是对于比较密集的POI,比如学校、超市这种,如果想尽可能将数据爬取完整,则需要缩小划分网格的经纬度间隔(las),这样就会增加爬取时间;另一种是,爬取所需时间还与检索的区域大小成正相关,检索区域越大的话,花费的时间就会越长;

(1)基本参数设置

lat_1 = 37.973948 #裕华区左下纬度 lon_1 = 114.382523 #新华区左下经度 lat_2 = 38.17371 #新华右上纬度 lon_2 = 114.714494 #长安区右上经度 las = 0.01 # 爬取时划分网格的经纬度间隔 ak='oieDykNvNYpiu7xe3tIuvFZmdfQQB4pt' # 根据自己在百度地图注册平台上申请的AK place = r'政府' # 爬取的POI类型,可以是学校、政府、超市、商场、小区、餐饮店等等,这里以政府为例

(2)POI区域划分,按照经纬度间隔0.01度,将不同URL存入一个列表中

print ('*******************{}POI开始获取****************'.format(place)) urls=[] #声明一个数组列表 lat_count=int((lat_2-lat_1)/las+1) lon_count=int((lon_2-lon_1)/las+1) for lat_c in range(0,lat_count): lat_b1=lat_1+las*lat_c for lon_c in range(0,lon_count): lon_b1=lon_1+las*lon_c for i in range(0,20): page_num=str(i) url='http://api.map.baidu.com/place/v2/search?query='+place+'& bounds='+str(lat_b1)+','+str(lon_b1)+','+str(lat_b1+las)+','+str(lon_b1+las)+'&page_size=20&page_num='+str(page_num)+'&output=json&ak='+ak urls.append(url) print ('url列表读取完成')

(3)为了方便看结果,这里设置些参数

total_before = 0 total = 0 # 获取总条数 label = 0 # 查看是否获取到数据的标志 count_20 = 0 # 查看每页超过20条的页数 K = 0 # 运行出错的断点数,代表第多少次循环 urls = urls[K:] # 防止出现意外,比如在百度地图获取的数据达到上限,就会终止服务,这里的K主要是为了从当前断点处继续爬取,所以需要根据自己程序终止断点设置; count_xunhuan = len(urls) # 循环次数 count_xunhuan2 = count_xunhuan # 还剩循环次数,主要让自己知道大概有多少循环,每次都打印下,对程序运行时间有个大致了解;

(4)创建文件以及将爬取到的数据读入文件

f=open(r''+place+'.csv','a',encoding='utf-8') # 根据爬取的POI类型创建文件 print("+++++++++++爬取需循环{}次++++++++++++".format(count_xunhuan)) for url in urls: #time.sleep(10) # 为了防止并发量报警,设置了一个10秒的休眠。认证后就不需要了 html = requests.get(url) # 获取网页信息 data = html.json() # 获取网页信息的json格式数据 total_before = total for item in data['results']: jname = item['name'] # 获取名称 jlat = item['location']['lat'] # 获取纬度 jlon = item['location']['lng'] # 获取经度 jadd = item['address'] # 获取详细地址 jpro = item['province'] # 获取所在省 jcity = item['city'] # 获取所在城市 jarea = item['area'] # 获取所在区或县 j_str = jname + ',' + str(jlat) + ',' + str(jlon) + ',' + jadd + ',' + jpro + ',' + jcity + ',' + jarea + '\n' # 以逗号格式,将数据存入一个字符串 f.write(j_str) # 将数据以行的形式写入CSV文件中 total = total + 1 # 获取的数据记录数 label = 1 # 表示每个小网格是否爬取到数据,如果为1,则表示获取到数据,执行下面的if语句,如果为0,则表示没有获取到数据; count_xunhuan2 = count_xunhuan2 - 1 # 循环次数减一,方便查看了解循环进度 if label == 1: print("需循环{}次, 已循环{}次, 还剩{}次循环结束".format(count_xunhuan, count_xunhuan-count_xunhuan2, count_xunhuan2)) print('新增{}条数据'.format(total-total_before)) if total-total_before == 20: count_20 = count_20 + 1 # 查看获取到20条数据的页数,因为百度地图限制每页获取20条数据,如果该网格区域超过的话,也是爬取到20条,所以这里设置count_20查看下没有爬取完整的网格数,如果过多,则最好修改las经纬度间隔; print("---------已获取{}条数据----------".format(total)) label = 0 print("每页新增超过20条的页数:{}".format(count_20)) f.close() print ('*****************{}POI获取完成******************'.format(place)) 5. 完整代码汇总

关于该案例的完整代码,见下,小伙伴们可以结合我写的注释共同食用;

#-*-coding:UTF-8-*- import sys import requests #导入requests库,这是一个第三方库,把网页上的内容爬下来用的 ty=sys.getfilesystemencoding() #这个可以获取文件系统的编码形式 ## 1. 基本参数设置: lat_1 = 37.444122 # 石家庄市左下纬度 lon_1 = 113.529103 # 新石家庄市左下经度 lat_2 = 38.768031 # 石家庄市右上纬度 lon_2 = 115.486183 # 石家庄市右上经度 las = 0.01 # 爬取时划分网格的经纬度间隔 ak='xxxxxxxx' # 根据自己在百度地图注册平台上申请的AK place = r'政府' # 爬取的POI类型,可以是学校、政府、超市、商场、小区、餐饮店等等,这里以政府为例 ## 2. POI区域划分,按照经纬度间隔0.01度,将不同URL存入一个列表中 print ('*******************{}POI开始获取****************'.format(place)) urls=[] #声明一个数组列表 lat_count=int((lat_2-lat_1)/las+1) lon_count=int((lon_2-lon_1)/las+1) for lat_c in range(0,lat_count): lat_b1=lat_1+las*lat_c for lon_c in range(0,lon_count): lon_b1=lon_1+las*lon_c for i in range(0,20): page_num=str(i) url='http://api.map.baidu.com/place/v2/search?query='+place+'& bounds='+str(lat_b1)+','+str(lon_b1)+','+str(lat_b1+las)+','+str(lon_b1+las)+'&page_size=20&page_num='+str(page_num)+'&output=json&ak='+ak urls.append(url) print ('url列表读取完成') ## 3. 为了方便看结果,这里设置了些参数 total_before = 0 total = 0 # 获取总条数 label = 0 # 查看是否获取到数据的标志 count_20 = 0 # 查看每页超过20条的页数 K = 0 # 运行出错的断点数,代表第多少次循环 urls = urls[K:] # 防止出现意外,比如在百度地图获取的数据达到上限,就会终止服务,这里的K主要是为了从当前断点处继续爬取,所以需要根据自己程序终止断点设置; count_xunhuan = len(urls) # 循环次数 count_xunhuan2 = count_xunhuan # 还剩循环次数,主要让自己知道大概有多少循环,每次都打印下,对程序运行时间有个大致了解; # 4. 创建文件以及将爬取到的数据读入文件 f=open(r''+place+'.csv','a',encoding='utf-8') # 根据爬取的POI类型创建文件 print("+++++++++++爬取需循环{}次++++++++++++".format(count_xunhuan)) for url in urls: #time.sleep(10) # 为了防止并发量报警,设置了一个10秒的休眠。认证后就不需要了 html = requests.get(url) # 获取网页信息 data = html.json() # 获取网页信息的json格式数据 total_before = total for item in data['results']: jname = item['name'] # 获取名称 jlat = item['location']['lat'] # 获取纬度 jlon = item['location']['lng'] # 获取经度 jadd = item['address'] # 获取详细地址 jpro = item['province'] # 获取所在省 jcity = item['city'] # 获取所在城市 jarea = item['area'] # 获取所在区或县 j_str = jname + ',' + str(jlat) + ',' + str(jlon) + ',' + jadd + ',' + jpro + ',' + jcity + ',' + jarea + '\n' # 以逗号格式,将数据存入一个字符串 f.write(j_str) # 将数据以行的形式写入CSV文件中 total = total + 1 # 获取的数据记录数 label = 1 # 表示每个小网格是否爬取到数据,如果为1,则表示获取到数据,执行下面的if语句,如果为0,则表示没有获取到数据; count_xunhuan2 = count_xunhuan2 - 1 # 循环次数减一,方便查看了解循环进度 if label == 1: print("需循环{}次, 已循环{}次, 还剩{}次循环结束".format(count_xunhuan, count_xunhuan-count_xunhuan2, count_xunhuan2)) print('新增{}条数据'.format(total-total_before)) if total-total_before == 20: count_20 = count_20 + 1 # 查看获取到20条数据的页数,因为百度地图限制每页获取20条数据,如果该网格区域超过的话,也是爬取到20条,所以这里设置count_20查看下没有爬取完整的网格数,如果过多,则最好修改las经纬度间隔; print("---------已获取{}条数据----------".format(total)) label = 0 print("每页新增超过20条的页数:{}".format(count_20)) f.close() print ('*****************{}POI获取完成******************'.format(place)) 6. 爬取的数据展示

从百度地图上获取到的POI数据,主要包括以下字段:POI名称、纬度、经度、详细地址、省份、市以及区共7个字段;数据展示如下:

好了,到这里为止,本文的所有内容就结束了。本文主要利用Python调用百度地图API接口,来爬取POI兴趣点数据,当初做练习的时候获取了政府、小区、餐饮店、KTV、医院、商场、超市、学校、写字楼等等POI数据;总体来说,本文的难度相对来说不大,主要在于理解上,因为这跟之前三篇爬虫案例有一些差别,体现在这次获取的是json格式的文件,不再是之前的html文件了,html网页文件需要通过xpath进行解析,而json文件中得数据都存储在字典中,通过遍历这些字典来获取对应的值;关于json文件中数据的获取,已经在代码里了,小伙伴们不懂得可以多看看代码理解下。

感谢小伙伴们看到这里,如果哪里写的不是很全面的地方,可以在评论区留言哦,我会不断完善的!如果小伙伴们对Python感兴趣的话,可以来波关注,后续还有很多干货呀!

本文的参考资料:

零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(基础篇)

零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(代码篇)

零基础掌握百度地图兴趣点获取POI爬虫(python语言爬取)(进阶篇)

                          来都来了,确定不留下点什么嘛,嘻嘻~

                                         

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3