数据爬取实战——POI爬取及数据可视化

​爬虫技术和GIS结合在一起可以碰撞出意想不到的火花,通过百度地图api/forlium/requests/wordcloud库可以爬取到感兴趣的POI数据,并直观地将其显示出来。本章通过requests库调用百度地图api爬取数据,并利用forlium库可视化数据,最后用wordcloud库统计出现频率最高的汉字,制作词云。

(1)调用百度地图api/requests库爬取POI数据

​requests库是爬虫技术中常用的开源库,常用来对浏览器发起请求进行访问。百度地图api开发文档见http://lbs.baidu.com/index.php?title=webapi/guide/webservice-placeapi。在创建ak秘钥后,我们就可以调用百度api的接口爬取数据,我们可以通过网页接口来执行get请求,爬取数据,这里给出地点检索的相关接口。

​具体代码方面,笔者采用了https://github.com/lbygg227/POI_GET的部分代码,感谢此人贡献的开源代码。代码主要使用了两种检索方法:1. 按行政区划检索POI,受到百度地图api的限制,最多开源读取20页数据,共计400条,所以这种方法比较适用于数量较小的POI爬取,不推荐在大的工程项目中使用。2. 按矩形框搜索,通过给出矩形框的左下角和右上角经纬度坐标,结合api进行爬取。

Method1

def method_region():

print("请输入所需要爬取数据的行政区划名称,如南京市,南京市鼓楼区等")

city=str(input())

print ('开始')

urls=[] #声明一个数组列表

for i in range(0,20):

page_num=str(i)

url='http://api.map.baidu.com/place/v2/search?query='+name+'&region='+city+'&page_size=20&page_num='+str(page_num)+'&output=json&ak='+ak

urls.append(url)

print ('url列表读取完成')

for url in urls:

getdata(url)

print('爬取中,请耐心等待')

f.close()

print ('完成,文件位于D盘目录下,请查看')

Method2

def method_bounds(): #读取POI更多,更细

print("请输入所选取地图矩形区域范围的左下角经度")

lat_l=float(input())

print("请输入所选取地图矩形区域范围的左下角纬度")

lng_l=float(input())

print("请输入所选取地图矩形区域范围的右上角经度")

lat_r=float(input())

print("请输入所选取地图矩形区域范围的右上角纬度")

lng_r=float(input())

a=(lat_l>=lat_r or lng_l>=lng_r)

while a:

print('经纬度输入错误,请重新输入')

print("请输入所选取地图矩形区域范围的左下角经度")

lat_l=float(input())

print("请输入所选取地图矩形区域范围的左下角纬度")

lng_l=float(input())

print("请输入所选取地图矩形区域范围的右上角经度")

lat_r=float(input())

print("请输入所选取地图矩形区域范围的右上角纬度")

lng_r=float(input())

a=(lat_l>lat_r or lng_l>lng_r)

lng_c=lng_r-lng_l

lat_c=lat_r-lat_l

#将研究区按经纬度步长大小划分若干块,存储每块左下、右上经纬度

lng_num=int(lng_c/0.1)+1 #经纬度跨步越小,采集数据越多

lat_num=int(lat_c/0.1)+1

arr=np.zeros((lat_num+1,lng_num+1,2)) #前两维是行数、列数,第三维是2元列表,经度、纬度

for lat in range(0,lat_num+1):

for lng in range(0,lng_num+1):

arr[lat][lng]=[lng_l+lng*0.1,lat_l+lat*0.1]

urls=[]

print('开始')

for lat in range(0,lat_num):

for lng in range(0,lng_num):

for b in range(0,20): #每块最多读取20页POI数据

page_num=str(b)

url='http://api.map.baidu.com/place/v2/search?query='+name+'&bounds='+str((arr[lat][lng][0]))+','+str((arr[lat][lng][1]))+','+str((arr[lat+1][lng+1][0]))+','+str((arr[lat+1][lng+1][1]))+'&page_size=20&page_num='+str(page_num)+'&output=json&ak='+ak

urls.append(url)

print ('url列表读取完成')

for url in urls:

getdata(url)

print('爬取中,请耐心等待')

f.close()

print ('完成,文件位于D盘目录下,请查看')

这里介绍一下Method2。它采用的思想主要是这样的,虽然我们对某一块区域爬取的数据数量受到了限制(400个/20页),但我们可以通过将一整块较大的矩形区域划分为若干小矩形块,然后分别遍历爬取从而解决这个问题。划分的步长越小(这里是0.1),爬取的越仔细,数据量也越大,亲测0.01时爬取的数据比前者多了2000+。在网格小到一定程度的时候,网格内的POI数量小于400,就不会受到20页等的限制了,这时爬取的数据较为详细。

上图是三维数组的形式,函数中用于存储经纬度的列表是三维数组,前两维是行数和列数,第三维存储了经纬度数据,具体存放形式如上图。

def getdata(url):

try:

socket.setdefaulttimeout(timeout) #设置间隔时间,防止爬取时被阻断

html=requests.get(url)

data=html.json()

if data['results']!=None:

for item in data['results']:

jname=item['name']#获取名称

jlat=item['location']['lat']#获取经纬度

jlon=item['location']['lng']

jarea=item['area']#获取行政区

jadd=item['address']#获取具体地址

j_str=jname+','+str(jlat)+','+str(jlon)+','+jarea+','+jadd+','+'\n'

f.write(j_str)

#time.sleep(1)

except:

getdata(url)

这段代码不用多说吧,requests发起get请求,解析数据为json格式,然后获取到名称、经纬度、行政区、地址等信息。

(2)制作heatmap

​获取这些数据后,为了进行数据的可视化,我们学习了forlium模块。

​folium是js上著名的地理信息可视化库leaflet.js为Python提供的接口,通过它,我们可以通过在Python端编写代码操纵数据,来调用leaflet的相关功能,基于内建的osm或自行获取的osm资源和地图原件进行地理信息内容的可视化,以及制作优美的可交互地图。其语法格式类似ggplot2,是通过不断添加图层元素来定义一个Map对象,最后以几种方式将Map对象展现出来。

import numpy as np

import pandas as pd

import folium

import webbrowser #浏览器

from folium.plugins import HeatMap

poi_file = 'D:\POI-res.txt'

p_lon =[]

p_lat =[]

poi_name =[]

f = open(poi_file, 'r', encoding='utf-8') #注意encoding='utf-8'

for line in f.readlines(): #逐行读取

line = line.split(',')

p_lon.append(line[2])

p_lat.append(line[1])

poi_name.append(line[0])

f.close()

data = [ [p_lat[i], p_lon[i]] for i in range(2000) ] #将数据制作成[lats,lons,weights]的形式,这里只显示2000个POI数据

map_osm = folium.Map(location=[1,2],zoom_start=5) #绘制Map,开始缩放程度是5倍

HeatMap(data).add_to(map_osm) # 绘制热力图,并将热力图添加到map里

file_path = r"D:/text.html"

map_osm.save(file_path) # 保存为html文件

webbrowser.open(file_path) # 默认浏览器打开

​如图,效果还算不错的嘛。

(3)wordcloud制作

​早就听过wordcloud的大名了,选取文本中的高频词汇,然后按频率分布制作词云。这里由于字符串属于中文字符,需要下载字体文件,进行解译(旁门左道字体)。

import wordcloud

#注:文件名不能为wordcloud.py,会导不了库

poi_name = []

f = open( 'D:\POI-res.txt','r',encoding='utf-8')

for line in f.readlines():

line = line.split(',')

poi_name.append(line[0])

text = str(poi_name) #列表转str字符串

wc = wordcloud.WordCloud(

background_color='white',

font_path="D:\Tablefile\POI_GET\POI_GET-master\PangMenZhengDaoBiaoTiTi-1.ttf" #添加字体,否则中文字体会乱码

)

wc.generate(text)

wc.to_file(r"D:\Tablefile\POI_GET\POI_GET-master\1.png")

​沙县小吃果然傲视群雄啊,哈哈。

​这里还介绍一下jieba库,这个库可以针对文本进行词义判断,从而进行划分,常常和wordcloud配合使用。

import jieba

seg_list = jieba.cut("他来到上海交通大学", cut_all=False)

print("【精确模式】:" + "/ ".join(seg_list))

​除了上述这些以外,我们还可以用百度地图api爬取感兴趣POI附近的POI数据。主要是获取感兴趣POI后,获取其位置,如按radius=500搜索该POI附近的其他POI。详细代码见博客https://www.cnblogs.com/IvyWong/p/11812412.html。

​另外说一句,本期博客是markdown编写的,格式确实相当不错呢!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐