初衷
老婆大人想知道金華地區(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

我們先設(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)容了。嗶嗶嗶。。