百度地图开放平台web api 获取上海市所有小区信息

百度地图开放平台web api 获取上海市所有小区信息

  • 背景
  • 问题思考
  • 具体操作
  • 编写程序
  • 代码解读
  • 拓展讨论
  • 数据清洗
  • 参考文献

背景

作为一家深耕房地产咨询服务行业十年之久的房地产大数据公司,竟然没有上海市的小区字典,实属遗憾,底层数据的收集以及标准化的建立迫在眉睫,这不,从其他平台来的数据永远无法判断正误与否,也无法与自己已有的数据形成清晰的映射关系,于是,上海市的小区信息库的建立是一项非常基础且重要的工程,如何才能建设好上海市的小区信息库(以下简称上海库),并使其具备具备以下基本要素

  1. 完备性
    这个很好理解,就是建立起来的小区库要涵盖上海市所有的小区,不能有遗漏。
  2. 唯一性
    这个也很好理解,就是库里面每一个小区都要有唯一identification,用以区别其他小区。
  3. 规范性
    库里面每一个小区名称都应该规范起来,同一个小区,不能一会叫“***花园”,一会又叫“**花苑”,在命名的时候应该充分考虑当地居民的叫法。
  4. 兼容性
    如果我这个库建好后,从第三方平台来的数据也要能够和该库对应起来,而第三方平台肯定是用的专业地图数据供应商的接口。
  5. 可更新性
    这个是为了以后出现小区合并或者老旧小区拆迁导致数据无意义,也为了后面新建的小区的纳入提供entrance。

问题思考

这是个历史问题,因为在新房的时候,只需要考虑哪一个物业,哪一期楼盘出售等,不需要考虑一期楼盘,二期楼盘形成的规模效应——小区,上海机构手里面虽然有一些数据,但是也无核对真实与否以及重复与否,同时从第三方获取的数据也存在同样问题,且这两方面来的数据无法形成某种清晰的映射关系,仔细研究的结论是自己这边缺少一个统一的标准的小区字典库,打个比方,倘若自己手里面有一本商务印书馆出版的汉字字典的话, 在遇到任何一个陌生的字,我都可以从这本字典里面查找。那么如何建立这样一本小区字典库呢? 我们知道地图数据服务商(百度地图,高德地图,腾讯地图,搜狗地图,360地图,谷歌地图,凯立德地图,老虎地图等)是专门做这个的,他们那里肯定有这个数据,他们只是把这些数据做成更加丰富的可视化可查询的专业级别的工具,倘若你精力无限,你肯定可以通过这些工具一个一个查找并录入进自己库,然而人为很难确保不重不漏,于是提出下面几个思路

  1. 根据有Heine—Borel有限覆盖定理,利用手里已有的数据不断提纯拼凑在一起去逼近整个上海库,具体见Selenium+webdriver自动化测试那一期的内容。
  2. 利用百度地图开放平台的web api接口获取整个上海市的小区数据,这也是本期我们重点讨论的。

具体操作

第一步 锁定问题及解决办法
简单归结到一点,我们是要从地图上面把小区拎出来,恰好百度地图开放平台提供了地点索引服务,百度地图开放平台的地点检索服务(又名Place API)是一类Web API接口服务;该服务提供多种场景的地点(POI)检索功能,包括城市检索、圆形区域检索、矩形区域检索。开发者可通过接口获取地点(POI)基础或详细地理信息。但是出于对数据保护目的,单次访问服务最多同时返回400条数据,此限制无法修改,请根据实际需求,通过添加分类、设置范围等方式,缩小检索范围。显然不能通过最细的region=上海市 来检索,于是转向后两者索引,这次探讨矩形区域检索的办法,矩形区域检索是通过设置检索区域左下角坐标和右上角坐标,使之构成一个矩形,然后检索矩形内的地点信息(常用于手机或PC端地图视野内检索),查阅信息得知上海的经纬度范围是东经120.86495至122.002349, 北纬30.606766至31.870185之间,当然,这里指的是经纬度最大跨度,我们也知道上海不可能呈现成一个规规矩矩的长方形,还是由于返回数据条数的限制,我们不可能一次性把矩形设置成这么大的,于是需要对上海进行栅格划分,划分成更小的小矩形,缩小检索范围。

第二步 划分小矩形
根据上海市总面积为6340平方千米,约等于 80 k m ∗ 80 k m 80km*80km 80km80km, 于是可以将经度的范围东经120.86495至122.002349切分成80份,维度的范围北纬30.606766至31.870185也切分成80份,相当于用一张80×80的细网去覆盖整个上海面积(这有点像高中时候测量分子直径的油膜实验),使得每一个小矩形南北纵向长度为 31.870185 − 30.606766 80 \frac{31.870185-30.606766}{80} 8031.87018530.606766
东西横向长度为
122.002349 − 120.86495 80 \frac{122.002349 -120.86495}{80} 80122.002349120.86495
有了小矩形的长和宽就可以利用递归的思想构造出这张细网每个小矩形左下角坐标和右上角坐标,使之成为矩形范围bounds的参数。

编写程序

有了思路和前期的准备工作,就可以进入编写调用百度地图web api程序了,具体代码如下

# -*- coding: utf-8 -*-
"""
Created on Fri Jul 12 12:59:48 2019
title:community_query
@author: Uncle Three
"""
import numpy as np #导入数值计算扩展模块
import requests #导入网页请求模块
unit=80 #6340≈80*80个栅格
lat_partion=[round(x,6) for x in list(np.linspace(30.606766,31.870185,unit))] #维度划分,保留6位小数
lng_partion=[round(y,6) for y in list(np.linspace(120.86495,122.002349,unit))] #经度划分,保留6位小数
print("connecting mysql……\n")
import pymysql #导入pymysql模块
db=pymysql.connect("localhost","root","123456awd","community",charset='utf8') #链接mysql数据库community
print("connect successfully,start creating table community_sh in database community\n")
cursor=db.cursor() #创建游标对象
###########华丽的分割线#############
cursor.execute("DROP TABLE IF EXISTS community_sh") #如果community_sh存在则drop掉
c_sql="""CREATE TABLE community_sh(
      province char(10),
      city char(10),
      area char(10),
      community char(30),
      address char(50),
      lat float(9,6),
      lng float(9,6),
      uid char(25),
      detail int(1)  
            )"""
cursor.execute(c_sql) #创建一个表
###########华丽的分割线#############
print("table community_sh has been created\n")
def get_community(): #自定义按区域获取小区名称的函数
    for i in range(0,79): #横向栅格索引
        for j in range(0,79): #纵向栅格索引
            url="http://api.map.baidu.com/place/v2/search?query=小区&tag=住宅区&page_size=20&page_num=0\
            &bounds="+str(lat_partion[i])+","+str(lng_partion[j])+","+str(lat_partion[i+1])+","+str(lng_partion[j+1])+\
            "&output=json&ak=你的ak" #构造请求网址,其中你的ak是你要向百度申请的密钥
            header={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'} #构造请求头
            response=requests.get(url,headers=header) #发出请求
            answer=response.json() #json化
            print("there are totally %s community in (%d,%d) rectangle\n"%(len(answer['results']),i,j))
            if answer['status']==0: #如果正常返回
                for k in range(len(answer['results'])): 
                    province=answer['results'][k]['province'] #省份
                    city=answer['results'][k]['city'] #城市
                    area=answer['results'][k]['area'] #区域
                    community=answer['results'][k]['name'] #小区名称
                    address=answer['results'][k]['address'] #地址
                    lat=answer['results'][k]['location']['lat'] #纬度
                    lng=answer['results'][k]['location']['lng'] #经度
                    uid=answer['results'][k]['uid'] #唯一标识
                    detail=answer['results'][k]['detail'] #是否具有详情内容
                    insert_data=("INSERT INTO community_sh(province,city,area,community,address,lat,lng,uid,detail)""VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s)")
                    community_data=([province,city,area,community,address,lat,lng,uid,detail]) #组成一条记录
                    cursor.execute(insert_data,community_data) #执行插入操作
                    db.commit() #主动提交
            else:
                print("the (%d,%d) rectangle contains no community"%(i,j))            
if __name__=='__main__':
    get_community()

代码解读

首先找出上海市经纬度范围,这里建议用百度拾取器的办法把上海市四至的经纬度取下来,毕竟用人家的数据,百度自己家的坐标系应该是统一的,建议不要去百度百科搜上海市的经纬度范围,会有很大误差的,因为百度给自己的地图产品坐标肯定是经过加密的。
百度地图开放平台web api 获取上海市所有小区信息_第1张图片
其次是构造了80*80个小矩形,然后将这些小矩形的坐标作为请求网址url的一部分参数,编写主函数get_community()来获取小矩形内的小区的信息,组合成省份,城市,区域,小区,地址,纬度,经度,唯一标识以及是否具有详细信息共9个字段,然后一条一条写入数据库MySQL里面去保存[^1],由于这样请求会返回大量数据,可以分次请求,比如设置

for i in range(0,10):
   for j in range(0,79):

然后再是10个等

for i in range(10,20):
   for j in range(0,79):

最后再是10个等

for i in range(70,79):
   for j in range(0,79):

通过限定横向栅格10个10个进行请求,在分次请求的时候,第一次运行程序要加上华丽分割线里面的代码,后面的请求请注释掉华丽分割线里面的代码 。百度地图开放平台对请求权益有限制,个人认证的可以每天30000配额, 未认证的每天才2000配额。
百度地图开放平台web api 获取上海市所有小区信息_第2张图片

拓展讨论

百度地图开放平台web api 获取上海市所有小区信息_第3张图片
上表是百度给出的poi分类标签,利用这个方法可以获取任何一座城市的poi地理信息,修改里面的大矩形范围就可以定位到新的城市,修改query字段查找想要查找的poi。比如我要查找杭州市的购物类的购物中心、百货商场、超市、便利店四类poi信息,首先查到杭州市的经纬度范围,然后设定query=购物中心$百货商场$超市$便利店,同时为此新建shopping_hz表,代码如下

lat_partion=[round(x,6) for x in list(np.linspace(29.010001,30.020001,unit))] #纬度划分
lng_partion=[round(y,6) for y in list(np.linspace(118.020001,120.030001,unit))] #经度划分
...
query=购物中心$百货商场$超市$便利店

数据清洗

由于是按照矩形区域请求的,其中不免会有周边其他省份(浙江,江苏)的数据,只需要select * from community_sh where city=‘上海市’ 便是。然后还需要去除重复数据,且要把***小区***号楼的数据全部规约到小区层级,百度地图的数据还是挺规范的,但是程序获取的并不一定是最新数据,需要人工进行核对和查漏补缺,另一个要合理设定上海市在百度地图坐标系下的经纬度范围。
百度地图开放平台web api 获取上海市所有小区信息_第4张图片

  1. 程序运行过程中出现 DataError:(1406:“Data too long for column address” at row 1) , 请进入mysql 里面设置SET @@global.sql_mode= ‘’; 运行。
  2. 如果出现 KeyError: ‘results’,那么就是额度用完了,洗洗睡吧,明天再来或者升级权益。

如果你不会写代码或只想要数据的话可以关注“三行科创”公众号,留个邮箱和数据名称,我发给你。

参考文献

1,百度坐标拾取器
2,百度地图开放平台
3,http://www.mapboxx.cn/article/bmappoi/

你可能感兴趣的:(网络爬虫)