初衷
老婆大人想知道金华地区的早教、培训机构相关的情况,老办法,爬起来。
百度地图api的限制
webapi/guide/webservice-placeapi - Wiki
查找了下大家获取百度POI数据的方法,知道了百度地图的地点检索api做了几个限制:
- 非认证个人开发者2k每日请求量,认证后个人开发者3w日请求量
- 检索接口最大返回670条结果 地点检索api分两类接口,一类是区域检索,第二类是详情检索,也就是说,如果我需要对每个地点去做详情检索,那就算3w的日请求量也远远不够了。好在我看区域检索接口内也可以返回较为详细的内容,基本够用。还有一个坑是区域检索接口是分页返回的,每页最多20条,所以这会消耗请求量。这里不考虑这些限制,大不了多跑几天。
实施方法
由于检索接口一次最多能返回670条数据,所以有个策略就是使用矩形检索接口,将矩形区域缩小到一定程度后,该区域的返回条目数不超过670条就可以,当然也不要太小,太小浪费请求量。
我先用一个大矩形划定我关心的区域,然后找到左上角和右下角的坐标:(这个可以手动从百度地图web站上获取到) 左下角,石门村:13312800.93,3357744.44 右上角,横山村:13334828.53,3373640.97
我们先设计10*10个小矩形来扫描。为了第二天继续爬取,每次移动记录当前矩形的位置。为了观察是否可能由于矩形过小导致数据丢失,记录每个矩形的获取数目,当等于670条时,可以重新细分扫描该类矩形区域。
注意 :上面拿到的坐标是百度地图米制坐标,需要转换成百度经纬度坐标才能用于检索api,转换api地址:webapi/guide/changeposition - Wiki
编码
由于是纯api爬取,就不上框架了。
#coding: utf-8
import requests
import json
import time
"""
查询关键字:
"""
FileKey = 'preclass'
KeyWord = u"早教$培训"
"""
关注区域的左下角和右上角百度地图坐标(经纬度)
"""
BigRect = {
'left': {
'x': 119.58962425017401,
'y': 29.02371358317696
},
'right': {
'x': 119.787499394624553,
'y': 29.149153586357146
}
}
"""
定义细分窗口的数量,横向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):
"""
获取小矩形的左上角和右下角坐标字符串(百度坐标系)
:param bigRect: 关注区域坐标信息
:param windowSize: 细分窗口数量信息
:param windowIndex: Z型扫描的小矩形索引号
: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()
复制代码
结果
发现我的需求,只要10*10就已经够用了。其实可以通过配置变量,查询想要查询的任何内容了。哔哔哔。。