• 百度地图POI爬取


    我们研究生的课程内容,做下笔记记录一下。

    使用的python环境是python3.7

    用的图大部分都是老师ppt里的图,懒得自己截了……

    申请百度开发者密匙

    (1)注册百度用户,注册过的话,直接登录就可以。登录地址为百度地图开放平台

    (2)登录后,在控制台点击【创建应用】。

    (3)填写表单,创建应用

    (4)这样就得到了API Key了

    百度地图搜索API语法

    poi查询的基本url为http://api.map.baidu.com/place/v2/search?

    按矩形框坐标范围检索

    有如下参数

    参数名 参数含义 类型 是否必须
    query 检索关键字,周边检索和矩形区域检索
    支持多关键字(以$隔开)并集检索,最多支持10个。
    string(45) Y
    bounds 设置查询的坐标范围
    矩形框的左下角经纬度和右上角经纬度。
    string(50) Y
    output 输出格式为json或xml string(50) Y
    scope 检索结果的详细程度
    取值为1或空,返回基本信息;取值2,返回详细信息
    string(50) N
    page_size 页面显示POI数量,默认值为10条,最大值为20 int N
    page_num 分页页码,从0开始 int N
    coord_type 坐标类型
    1:WGS84即GPS经纬度坐标
    2:国家测绘局GCJ-02坐标
    3:bd09,即百度经纬度坐标
    4:bd09mc即百度米坐标
    int N
    ret_coordtype 返回国测局经纬度坐标 string(50) N
    ak 开发者访问密钥(刚刚申请的API KEY) string(50) Y

    关于上面这个bounds的值,可以使用百度地图自己提供的坐标拾取器获得。也可以通过别人写的一个网页获得具体的行政区矩形框。

    别人写的网页的html如下

    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
        <style type="text/css">
            body, html,#allmap { 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}
            #panel{
                position:absolute;
                left:5px;
                top:5px;
            }
            #result{
                background: #fff;
                padding:5px;
            }
        </style>
        <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ"></script>
        <title>添加行政区划</title>
    </head>
    <body>
        <div id="allmap"></div>
        <div id="panel">
            <div>
            <input type="text" id="keyword" value="昆明市"/>
            <input type="button" value="查看范围" id="commitBtn"/>
            边界经纬度坐标
            <textarea id="pathStr"></textarea>
            边界墨卡托坐标
            <textarea id="pathMc"></textarea>
            </div>
            <div id="result">
            </div>
        </div>
    </body>
    </html>
    <script type="text/javascript">
        // 百度地图API功能
        var map = new BMap.Map("allmap");
        map.centerAndZoom(new BMap.Point(116.403765, 39.914850), 5);
        map.enableScrollWheelZoom();
        var mercatorProjection = map.getMapType().getProjection();
        $("#commitBtn").bind('click', function(){
            getBoundary($("#keyword").val());
        });
        function getBoundary(city){
            var bdary = new BMap.Boundary();
            bdary.get(city, function(rs){       //获取行政区域
                map.clearOverlays();        //清除地图覆盖物
                var count = rs.boundaries.length; //行政区域的点有多少个
                if (count === 0) {
                    alert('未能获取当前输入行政区域');
                    return ;
                }
                var pointArray = [];
                for (var i = 0; i < count; i++) {
                    var ply = new BMap.Polygon(rs.boundaries[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物
                    map.addOverlay(ply);  //添加覆盖物
                    pointArray = pointArray.concat(ply.getPath());
                }
                var pathStr = "";
                var pathMc = "";
                for (var i = 0; i < pointArray.length; i++) {
    
                    var mc = mercatorProjection.lngLatToPoint(pointArray[i]);
                    pathStr += pointArray[i].lng + "," + pointArray[i].lat + ";";
                    pathMc += mc.x + "," + mc.y + ";";
                }
                $('#pathStr').html(pathStr);
                $('#pathMc').html(pathMc);
                var ply = new BMap.Polygon(pointArray , {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物
                var bounds = ply.getBounds();
                var ne = bounds.getNorthEast();
                var sw = bounds.getSouthWest();
                var neMc = mercatorProjection.lngLatToPoint(ne);
                var swMc = mercatorProjection.lngLatToPoint(sw);
                var str = "经纬度:左下角,右上角:" + sw.lng + "," + sw.lat + ";" + ne.lng + "," + ne.lat
                                                     + "<br/>墨卡托坐标:左下角,右上角:" + swMc.x + "," + swMc.y + ";" + neMc.x + "," + neMc.y;
                $('#result').html(str);
                console.log(bounds);
                map.setViewport(pointArray);    //调整视野
            });
        }
        //getBoundary('北京');
    </script>
    

    打开后,类似这样

    按城市检索

    刚刚说的是按bound检索。也可以使用region参数按城市检索,比如说

    http://api.map.baidu.com/place/v2/search?query=公园&region=武昌区&output=json&ak=xxxxxxxxx&page_size=20&page_num=0
    

    周边检索

    使用location参数(设置中心点坐标)和radius参数(设置半径)进行周边检索。比如说

    http://api.map.baidu.com/place/v2/search?query=酒店&location=30.531642,114.366409&radius=300&output=json&ak=xxxxxxxxxx&page_size=20&page_num=0
    

    返回内容

    返回的内容,status为状态(0为成功返回,其他为异常),message为提示信息,total为返回的poi数量,results为当前页的poi信息。

    {
        "status":0,
        "message":"ok",
        "total":57,
        "results":[]     
     }
    

    一般scope为1的poi信息如下

     {
     	"name":"四美塘",
        "location":{
            "lat":30.603315,
            "lng":114.344284
         },
         "address":"武汉市武昌区和平大道589号(长江二桥下)",
         "province":"湖北省",
         "city":"武汉市",
         "area":"武昌区",
         "street_id":"15a93810075519bd15c8f6b4",
         "detail":1,
         "uid":"f21f49135aad2a3ce856fad9"
    }
    

    一般scope为2的poi信息如下

    {
     	"name":"四美塘",
        "location":{
            "lat":30.603315,
            "lng":114.344284
         },
         "address":"武汉市武昌区和平大道589号(长江二桥下)",
         "province":"湖北省",
         "city":"武汉市",
         "area":"武昌区",
         "street_id":"15a93810075519bd15c8f6b4",
         "detail":1,
         "uid":"f21f49135aad2a3ce856fad9"
         "detail_info":{
             "tag":"旅游景点;公园",
             "navi_location":{
                 "lng":114.34584347395,
                 "lat":30.604976470638
             },
             "type":"scope",
             "detail_url":"http://api.map.baidu.com/place/detail?uid=f21f49135aad2a3ce856fad9&output=html&source=placeapi_v2",
             "overall_rating":"4.3",
             "comment_num":"14",
             "children":[
                        
             ]
         }
    }
    

    百度地图POI搜索爬虫设计

    由于百度地图限制每次返回的poi最多只有400个,所以我们要把矩形区域进行分割以获得更多的数据。

    具体实现的代码如下

    def splitArea(bound):
        # 分割矩形区域的函数
        boundList=[]
        row_num = 2 # 按照 2 X 2 进行分割
        bound=list(map(float,bound.split(',')))
        step_lat = (bound[2] - bound[0]) / row_num
        step_lon = (bound[3] - bound[1]) / row_num
        for i in range(0,row_num):
            for j in range(0,row_num):
                boundTemp = []
                boundTemp.append(bound[0]+step_lat*i)
                boundTemp.append(bound[1]+step_lon*j)
                boundTemp.append(bound[0]+step_lat*(i+1))
                boundTemp.append(bound[1]+step_lon*(j+1))
                boundTemp=",".join(["%s" %x for x in boundTemp])
                boundList.append(boundTemp)
        return boundList
    

    课后作业完成

    题目如下:

    寻找武汉市中学(或小学)周围500米(或其他)内的网吧
    要求:提交代码py文件及运行结果文件(txt),txt文件格式如下:
    1,XXX小学
    1-1,XXX网吧
    1-2, XXX网吧
    2,XXX小学
    2-1,XXX网吧
    2-2, XXX网吧
    ………..

    写的python3代码为

    # coding:utf-8
    # version:python3.7
    # author:Ivy
    
    ############# 程序功能 ####################
    #    本程序用来获取百度地图某城市某种poi周围一定范围内的另一种类型的poi数据
    #    如示例是获取武汉市中学周围500米内的网吧
    #    生成的info.txt格式如下
    #     1, xxx中学
    #     1-1, xxx网吧
    #     1-2, xxx网吧
    #     2, xxx中学
    #     2-1, xxx网吧
    ############################################
    
    import requests,json
    import time,sys
    
    ############### 自主设置区 ###############
    ak = 'xxxxxxxxx' #API key
    keyword="中学"
    keyword2="网吧"
    radius=500
    city="武汉市"
    baseBound = '29.972898,113.707695,31.367052,115.085775' #武汉市矩形框的左下角经纬度和右上角经纬度
    ############################################
    
    
    # 构造header
    headers={
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
            'Accept-Encoding':'gzip, deflate',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'Host': 'api.map.baidu.com',
            'Upgrade-Insecure-Requests': '1',
            'Connection': 'keep-alive',
            'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
            'DNT': '1'
    }
    
    baseUrl="http://api.map.baidu.com/place/v2/search?query={}&bounds={}&page_size=20&page_num={}&output=json&ak={}"
    searchBaseUrl="http://api.map.baidu.com/place/v2/search?query={}&location={}&radius={}&output=json&ak={}&page_size=20&page_num={}"
    
    def req(url):
        # 访问url获取返回的数据读取为json
        errorNum=0
        while True:
            try:
                res = requests.get(url,headers=headers)
                res.encoding='utf-8'
                jd = json.loads(res.text)
                return jd
            except Exception as e:
                errorNum+=1
                print("出错了!")
                print(e)
                if errorNum==5:
                    sys.exit(1)
                time.sleep(1)
    
    def splitArea(bound):
        # 分割矩形区域的函数
        boundList=[]
        row_num = 2 # 按照 2 X 2 进行分割
        bound=list(map(float,bound.split(',')))
        step_lat = (bound[2] - bound[0]) / row_num
        step_lon = (bound[3] - bound[1]) / row_num
        for i in range(0,row_num):
            for j in range(0,row_num):
                boundTemp = []
                boundTemp.append(bound[0]+step_lat*i)
                boundTemp.append(bound[1]+step_lon*j)
                boundTemp.append(bound[0]+step_lat*(i+1))
                boundTemp.append(bound[1]+step_lon*(j+1))
                boundTemp=",".join(["%s" %x for x in boundTemp])
                # 是否要继续分割
                if check(boundTemp):
                    boundList.append(boundTemp)
                else:
                    boundList+=splitArea(boundTemp)
        return boundList
    
    def check(bound):
        # 检查一下当前矩形框是否需要再次分割
        checkUrl=baseUrl.format(keyword,bound,0,ak)
        checkJd=req(checkUrl)
        if checkJd["total"]<400:
            return True
        return False
    
    def getPois(bound):
        # 获取一定范围内所有的想要的poi的信息
        poiListTemp=[]
        pageNum=0
        while True:
            getUrl=baseUrl.format(keyword,bound,pageNum,ak)
            getJd=req(getUrl)
            if getJd['results']==[]: # 如果这一页没数据了就是结束啦,退出循环
                break
            for result in getJd['results']:
                if result["city"] != city: # 在矩形框中,但是不是想要的城市的,就不要记录啦
                    continue
                poiListTemp.append(result)
            pageNum+=1
        print("当前区域{}的poi已爬完,共有{}个".format(bound,len(poiListTemp)))
        return poiListTemp
    
    def searchPois(poiInfo):
        # 获取某个poi周边的其他poi信息
        poiListTemp=[]
        pageNum=0
        while True:
            searchUrl=searchBaseUrl.format(keyword2,str(poiInfo['location']['lat'])+','+str(poiInfo['location']['lng']),radius,ak,pageNum)
            searchJd=req(searchUrl)
            if searchJd['results']==[]:
                break
            for result in searchJd['results']:
                poiListTemp.append(result["name"])
                print(result["name"]," 已添加")
            pageNum+=1
        print(poiInfo["name"],"周边poi已爬完")
        return [poiInfo["name"],poiListTemp]
    
    if __name__ == '__main__':
        # 分割想要的区域
        boundList=splitArea(baseBound)
        print(boundList)
    
        # 获取范围内所有的poi信息
        poiList=[]
        for bound in boundList:
            poiList+=getPois(bound)
        print('已获得所有poi信息')
    
        # 对于获取到的每个poi进行周边检索
        infoList=[]
        for poi in poiList:
            infoList.append(searchPois(poi))
        print('已获得所有周边poi信息')
    
        # 把获取到的信息都写入txt
        with open('info.txt','w') as f:
            for i in range(len(infoList)):
                f.write(str(i+1)+', '+infoList[i][0]+'
    ')
                for j in range(len(infoList[i][1])):
                    f.write(str(i+1)+'-'+str(j+1)+', '+infoList[i][1][j]+'
    ')
        print("所获得的信息已全部写入txt啦!")
    

    【参考】

    [1]我们老师的PPT(不便公开)

  • 相关阅读:
    Spring 依赖注入:简单的HelloWorld例子
    浮动元素margin负值的应用
    小球拖动吸附
    三栏布局
    ES6学习之路1
    绝对定位模拟固定定位效果...
    jQuery中的一些小技巧
    探究css帧动画setps()用处
    你所不知道的cursor妙用
    正则表达式
  • 原文地址:https://www.cnblogs.com/IvyWong/p/11812412.html
Copyright © 2020-2023  润新知