爬取百度地图 POI 数据

初衷

老婆大人想知道金华地区的早教、培训机构相关的情况,老办法,爬起来。

百度地图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


爬取百度地图 POI 数据_第1张图片
image.png

我们先设计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就已经够用了。其实可以通过配置变量,查询想要查询的任何内容了。哔哔哔。。

你可能感兴趣的:(爬取百度地图 POI 数据)