數據爬取實戰——POI爬取及數據可視化


數據爬取實戰——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請求,爬取數據,這里給出地點檢索的相關接口。

image-20200527161329334

image-20200527161547658image-20200527161619213

image-20200527161659672

​ 具體代碼方面,筆者采用了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頁等的限制了,這時爬取的數據較為詳細。

image-20200527163322344

上圖是三維數組的形式,函數中用於存儲經緯度的列表是三維數組,前兩維是行數和列數,第三維存儲了經緯度數據,具體存放形式如上圖。

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格式,然后獲取到名稱、經緯度、行政區、地址等信息。

image-20200527163926660

(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)  # 默認瀏覽器打開

image-20200527164649740

​ 如圖,效果還算不錯的嘛。

(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")

image-20200527165117877

​ 沙縣小吃果然傲視群雄啊,哈哈。

​ 這里還介紹一下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編寫的,格式確實相當不錯呢!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM