數據爬取實戰——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+'®ion='+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編寫的,格式確實相當不錯呢!