美文网首页爬虫专题roubo的爬虫
爬取百度地图 POI 数据

爬取百度地图 POI 数据

作者: 萝卜日志 | 来源:发表于2018-08-31 15:56 被阅读60次

    初衷

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

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


    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 数据

        本文链接:https://www.haomeiwen.com/subject/suzswftx.html