爬取百度地圖 POI 數(shù)據(jù)

初衷

老婆大人想知道金華地區(qū)的早教、培訓(xùn)機(jī)構(gòu)相關(guān)的情況,老辦法,爬起來(lái)。

百度地圖api的限制

webapi/guide/webservice-placeapi - Wiki

查找了下大家獲取百度POI數(shù)據(jù)的方法,知道了百度地圖的地點(diǎn)檢索api做了幾個(gè)限制:

  • 非認(rèn)證個(gè)人開(kāi)發(fā)者2k每日請(qǐng)求量,認(rèn)證后個(gè)人開(kāi)發(fā)者3w日請(qǐng)求量
  • 檢索接口最大返回670條結(jié)果
    地點(diǎn)檢索api分兩類(lèi)接口,一類(lèi)是區(qū)域檢索,第二類(lèi)是詳情檢索,也就是說(shuō),如果我需要對(duì)每個(gè)地點(diǎn)去做詳情檢索,那就算3w的日請(qǐng)求量也遠(yuǎn)遠(yuǎn)不夠了。好在我看區(qū)域檢索接口內(nèi)也可以返回較為詳細(xì)的內(nèi)容,基本夠用。還有一個(gè)坑是區(qū)域檢索接口是分頁(yè)返回的,每頁(yè)最多20條,所以這會(huì)消耗請(qǐng)求量。這里不考慮這些限制,大不了多跑幾天。

實(shí)施方法

由于檢索接口一次最多能返回670條數(shù)據(jù),所以有個(gè)策略就是使用矩形檢索接口,將矩形區(qū)域縮小到一定程度后,該區(qū)域的返回條目數(shù)不超過(guò)670條就可以,當(dāng)然也不要太小,太小浪費(fèi)請(qǐng)求量。

我先用一個(gè)大矩形劃定我關(guān)心的區(qū)域,然后找到左上角和右下角的坐標(biāo):(這個(gè)可以手動(dòng)從百度地圖web站上獲取到)
左下角,石門(mén)村:13312800.93,3357744.44
右上角,橫山村:13334828.53,3373640.97


image.png

我們先設(shè)計(jì)10*10個(gè)小矩形來(lái)掃描。為了第二天繼續(xù)爬取,每次移動(dòng)記錄當(dāng)前矩形的位置。為了觀察是否可能由于矩形過(guò)小導(dǎo)致數(shù)據(jù)丟失,記錄每個(gè)矩形的獲取數(shù)目,當(dāng)?shù)扔?70條時(shí),可以重新細(xì)分掃描該類(lèi)矩形區(qū)域。

注意 :上面拿到的坐標(biāo)是百度地圖米制坐標(biāo),需要轉(zhuǎn)換成百度經(jīng)緯度坐標(biāo)才能用于檢索api,轉(zhuǎn)換api地址:webapi/guide/changeposition - Wiki

編碼

由于是純api爬取,就不上框架了。

#coding: utf-8
import requests
import json
import time


"""
    查詢(xún)關(guān)鍵字:
"""
FileKey = 'preclass'
KeyWord = u"早教$培訓(xùn)"

"""
    關(guān)注區(qū)域的左下角和右上角百度地圖坐標(biāo)(經(jīng)緯度)
"""
BigRect = {
    'left': {
        'x': 119.58962425017401,
        'y': 29.02371358317696
    },
    'right': {
        'x': 119.787499394624553,
        'y': 29.149153586357146
    }
}

"""
    定義細(xì)分窗口的數(shù)量,橫向X * 縱向Y
"""
WindowSize = {
    'xNum': 10.0,
    'yNum': 10.0
}


def getBaiduApiAk():
    """
    獲取配置文件中百度apikey:
     { "baiduak":"xx"}
    :return: str
    """
    with open("./config.json", "r") as f:
        config = json.load(f)
        return config["baiduak"]

def getSmallRect(bigRect, windowSize, windowIndex):
    """
    獲取小矩形的左上角和右下角坐標(biāo)字符串(百度坐標(biāo)系) 
    :param bigRect: 關(guān)注區(qū)域坐標(biāo)信息
    :param windowSize:  細(xì)分窗口數(shù)量信息
    :param windowIndex:  Z型掃描的小矩形索引號(hào)
    :return: lat,lng,lat,lng
    """
    offset_x = (bigRect['right']['x'] - bigRect['left']['x'])/windowSize['xNum']
    offset_y = (bigRect['right']['y'] - bigRect['left']['y'])/windowSize['yNum']
    left_x = bigRect['left']['x'] + offset_x * (windowIndex % windowSize['xNum'])
    left_y = bigRect['left']['y'] + offset_y * (windowIndex // windowSize['yNum'])
    right_x = (left_x + offset_x)
    right_y = (left_y + offset_y)
    return str(left_y) + ',' + str(left_x) + ',' + str(right_y) + ',' + str(right_x)


def requestBaiduApi(keyWords, smallRect, baiduAk, index, fileKey):
    today = time.strftime("%Y-%m-%d")
    pageNum = 0
    logfile = open("./log/" + fileKey + "-" + today + ".log", 'a+', encoding='utf-8')
    file = open("./result/" + fileKey + "-" + today + ".txt", 'a+', encoding='utf-8')
    # print('-------------')
    # print(index)
    while True:
        try:
            URL = "http://api.map.baidu.com/place/v2/search?query=" + keyWords + \
                "&bounds=" + smallRect + \
                "&output=json" +  \
                "&ak=" + baiduAk + \
                "&scope=2" + \
                "&page_size=20" + \
                "&page_num=" + str(pageNum)
            # print(pageNum)
            # print(URL)
            resp = requests.get(URL)
            res = json.loads(resp.text)
            # print(resp.text.strip())
            if len(res['results']) == 0:
                logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " stop " + str(index) + " " + smallRect + " " + str(pageNum) + '\n')
                break
            else:
                for r in res['results']:
                    # print(r)
                    file.writelines(str(r).strip() + '\n')
            pageNum += 1
            time.sleep(1)
        except:
            print("except")
            logfile.writelines(time.strftime("%Y%m%d%H%M%S") + " except "  + str(index) + " " + smallRect + " " + str(pageNum) + '\n')
            break


def main():
    baiduAk = getBaiduApiAk()
    for index in range(int(WindowSize['xNum'] * WindowSize['yNum'])):
        smallRect = getSmallRect(BigRect, WindowSize, index)
        requestBaiduApi(keyWords=KeyWord, smallRect=smallRect, baiduAk=baiduAk, index=index, fileKey=FileKey)
        time.sleep(1)


if __name__ == '__main__':
    main()

結(jié)果

發(fā)現(xiàn)我的需求,只要10*10就已經(jīng)夠用了。其實(shí)可以通過(guò)配置變量,查詢(xún)想要查詢(xún)的任何內(nèi)容了。嗶嗶嗶。。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容