零基礎掌握百度地圖興趣點獲取POI爬蟲(python語言爬取)(進階篇)


好,現在進入進階篇教程。 
1.獲取昆明市的bounds值 
看到下面這個東西了吧?在文本框里寫入昆明市,或者其他的行政區划地名,北京市、朝陽區、大河溝子村什么的。 
這也是通過調用百度地圖開放平台API實現的。 

實現起來很簡單,把下面這段代碼復制到一個txt文件中,然后把txt文件的拓展名改成html,用瀏覽器打開,就可以查詢行政區划的范圍了。

<!DOCTYPE html>  
<html>  
<head>  
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />  
    <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>

HTML是一個很神奇的格式,代碼可以在文本文檔里寫,不過應該很少有人用文本文檔寫代碼吧。 
這段代碼可以直接拿來用,也是我從網上找的,就不分析了。

2.更合理的對查詢范圍進行切分。 
這是一個很常見的問題,我們要采集昆明市的中學興趣點,然后就把矩形等分了六份,遍歷一遍(其實要不遺漏的獲取興趣點,只切六個絕對不夠用)。 
這種切分方式其實並不完美,分析一下,中心城區興趣點肯定分布更密集,而郊區會稀疏一些,而且一個城市的邊界不可能是矩形的,我們用一個大矩形切分,會爬取到一些其他城市的興趣點。 
有相關從業經驗的人應該能想到,如果有這個城市的分幅格網,遍歷分幅格網來爬取興趣點會更好一些,當然分幅格網我們也可以自己生成,根據預估的興趣點疏密程度,中心城區格網加密一些,外圍合並一些。 
arcgis有一個工具fishnet(創建漁網),可以生成格網。 
四至坐標可以在字段計算器里計算,選用python語言

minX=!shape.extent.xmin!
maxX=!shape.extent.xmax!
minY=!shape.extent.ymin!
maxY=!shape.extent.ymax!

之前寫過一篇畫分幅格網的教程:http://blog.sina.com.cn/s/blog_4c6ee7230102v590.html

把arcgis屬性表導出,可以用excel打開。 
用這個坐標列表去生成url列表,主要就是open和write語句的運用,其實都可以直接在excel里用函數去生成url列表。 
python逐行讀入:

f=open(路徑,’a’)
f.readlines()

3.python中文亂碼問題解決 
其實這是一個很棘手的問題。對於初學者來說,遇到亂碼問題,如何解決是一個幸運度的問題。有可能運氣不好,就解決不了了,有可能運氣夠好,根本遇不上。 
在亂碼問題上,python2.7要多於python3 
我精心挑選了幾個我覺得講得比較好的文章,大家可以參考一下,網上相關的文章太多了,容易懵。 
這篇中文亂碼的解決方式: 
http://www.jb51.net/article/42707.htm 
用sys.getfilesystemencoding()獲取文件系統的編碼方式。

import sys
type = sys.getfilesystemencoding()
print mystr.decode('utf-8').encode(type)

這篇文章寫得比較細: 
http://blog.csdn.net/ggggiqnypgjg/article/details/53271541

我的經驗是,如果遇到亂碼,怎么都調試不成功,那么可以換一個編譯環境,從python2.7換到python3,或者用別的函數,例如不用urllib2,用beautiful soul,或者requests等,有可能莫名其妙亂碼的問題就不出現了呢!

4.並發量限制與延時功能

import time
time.sleep(10)

這兩行代碼的意思是,延時10秒中,它解決的是一分鍾內並發量限制的問題,如果我們沒有做認證,那么一分鍾能訪問API服務的次數不超過幾次,超過了就會提示訪問超過並發量,就爬不到數據了。 
解決這個問題的方法就是用time.sleep(10),每次循環都先休眠10秒,就不會訪問超過並發量了。 
我覺得10秒夠用了,所以就設定了10秒,不過如果認證過的開發者,應該不會遇到並發量限制的問題。 
這段代碼示例一下:

import time
time.sleep(10)
print time.time()

休眠十秒,打印當前時間,結果是一個浮點秒數。 
5.把代碼移植到python3中。 
python2.7和python3略微有些差異,python3版本有一個好處就是中文亂碼的情況沒那么多,而且很多功能和第三方庫都被整合了。 
下面這段代碼是python3中的,可以看出比較於python2.7版本,進行了幾處改變,requests、webbrowser、列表的引用等。

#-*-coding:UTF-8-*- 
import json
import sys
import requests  #導入requests庫,這是一個第三方庫,把網頁上的內容爬下來用的
ty=sys.getfilesystemencoding()  #這個可以獲取文件系統的編碼形式
import time
lat_1=24.390894
lon_1=102.174112
lat_2=26.548645
lon_2=103.678942   #坐標范圍
las=1  #給las一個值1
ak='9s5GSYZsWbMaFU8Ps2V2VWvDlDlqGaaO'
push=r'D:\python\zwzwlast.txt'
#我們把變量都放在前面,后面就不涉及到變量了,如果要爬取別的POI,修改這幾個變量就可以了,不用改代碼了。


print (time.time())  #相較於python2.7,,python3print 需要加括號。
print ('開始')
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=中學& 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)
#urls.append(url)的意思是,將url添加入urls這個列表中。

f=open(r'D:\python\kunmingxuexiao.txt','a',encoding='utf-8')

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

for url in urls:
    time.sleep(10) #為了防止並發量報警,設置了一個10秒的休眠。
    html=requests.get(url)#獲取網頁信息
    data=html.json()#獲取網頁信息的json格式數據
    for item in data['results']:
        jname=item['name']
        jlat=item['location']['lat']
        jlon=item['location']['lng']
        jadd=item['address']
        j_str=jname+','+str(jlat)+','+str(jlon)+','+jadd+'\n'
        f.write(j_str)
    print (time.time())
f.close()
print ('完成')    

6.代碼簡化問題。 
可以通過分析json數據提煉一下簡化方式。 
網頁打開之后,看一下。 

“status”:0, 
“message”:”ok”, 
“total”:400, 
“results”:[ 

“name”:”北大附中雲南實驗學校”, 
“location”:{ 
“lat”:25.009573, 
“lng”:102.723208 
}, 
“address”:”昆明市日新中路北大附中雲南實驗學校”, 
“street_id”:”beabdbf4ac3394997069d3c7”, 
“detail”:1, 
“uid”:”beabdbf4ac3394997069d3c7” 
主要看results上面那三行,其中total,是這個矩形范圍內有多少個興趣點,有400個,說明我矩形畫大了,應該再細分一下,這個數應該小於400,因為我們之前已經說過20頁網址最多爬400個poi,total是400,說明實際的poi數量是大於400個的。 
提取某一坐標范圍中第一頁數據的時候,我們可以先獲取total值,這樣就可以計算出page_num的值了,如果total是100,那么只循環到第五頁,page_num=4的時候就可以了。 
這是代碼的一個簡化方式。 
當然應該還有很多,有待我們發覺。

7.過程參考資料匯總 
因為使用python的人很多,所以網上的資料也很多,不過要找到比較合適的,也沒那么容易。但是說實話,走彎路、排除錯誤答案也是學習收獲的一個過程,實踐過錯的,才知道什么是對的嘛! 
最有用的參考資料,應該是還算是官網。 
https://www.python.org/ 
學好英語很必要。 
這個網址,講的是requests的用法。 
http://docs.python-requests.org/zh_CN/latest/user/quickstart.html 
當然,買本書應該也不錯。 
然后腳本之家和CSDN都有很好的教程,不過也不是所有教程都是對的,多看多試多思考多練習,就會有多獲得。 
最后會再來一篇python安裝和第三方庫安裝篇,這個python的零基礎教程就先告一段落吧!

 

轉自----------------http://blog.csdn.net/sinat_41310868/article/details/78746251


免責聲明!

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



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