POI数据,英文全称Point of Intersesting,中文的意思是兴趣点,指的是在地图上有意义的点:比如商店、酒吧、加油站、医院、车站等。POI数据能够赋能时空行为、城市规划、地理信息等研究,因此获取准而全的POI数据是开展科研工作的基础。

高德POI大类有汽车服务 汽车销售 汽车维修 摩托车服务 餐饮服务 购物服务 生活服务 体育休闲服务 医疗保健服务 住宿服务 风景名胜 商务住宅 政府机构及社会团体 科教文化服务 交通设施服务 金融保险服务 公司企业 道路附属设施 地名地址信息 公共设施 事件活动 室内设施 通行设施。

首先要在高德开放平台申请key,每天各个接口查询都有限额,一般来讲够用。

本文以抓取成都市的餐饮服务业为例讲述。

这里调用高德的多边形接口来选定区域。

需要注意的大前提是高德官方文档的这句话:“另外,无论您指定多少个type,每次请求最多返回1000个POI信息,若场景需要获取更可能多的POI;建议您不要在type之中指定过多的类别,而是分多次请求从而得到更加准确的结果。

也就是我们每次请求一个区域内最多返回1000条数据,所以为了得到全部数据,当返回数据大于1000时,必须把该区域拆分为多个区域再次查询,如果拆分的区域里仍然大于1000,则继续拆分,就是一个递归的过程,另外实际上最大值实测用800条更稳妥。这一块递归拆分的算法是全过程技术难度最大的算法,我是搞不定了,还好网上有大佬现成的算法拿来用就行。

多边形搜索POI这个接口关键点就是传入好网格经纬度参数,传入矩形左上角和右下角经纬度即可。

首先我们要获取成都市的区域边界,这也是一个多边形经纬度,高德官方也有这个接口。

https://restapi.amap.com/v3/config/district?keywords=%E6%88%90%E9%83%BD%E5%B8%82&subdistrict=0&extensions=all&key=<你的key>

把得到的这些坐标保存到桌面txt文件,只取得"polyline"中的经纬度即可。这些坐标中还会有“|”,把它换为换行。最终如图:

OK准备工作完成可以开始在Python里拆分区域网格了。

#这些包该导入的就导入 pip python install xxxxx

import requests

from shapely.geometry import Polygon

from shapely import wkt

from requests.adapters import HTTPAdapter

s = requests.Session()

s.mount('http://', HTTPAdapter(max_retries=3))#设置重试次数为3次

s.mount('https://', HTTPAdapter(max_retries=3))

# 获取矩形搜索到poi的count数量

# key,是高德地图的key

# r是矩形,[minlng,minlat,maxlng,maxlat]

def countSearchPoi(key,r):

minlng = r[0]

minlat = r[1]

maxlng = r[2]

maxlat = r[3]

#高德接口有bug,用中文类别搜比数字类别代码结果多,这是一个小坑

url = 'https://restapi.amap.com/v3/place/polygon?types=餐饮服务&offset=20&page=1&extensions=all&output=json&polygon='\

+ str(minlng) +',' + str(minlat) + '|' + str(maxlng) + ',' + str(maxlat) + '&key=' + key

count = -1

#print(url)

try:

r = s.get(url, timeout=5)

data = r.json()

if 'status' in data:

if data['status'] == '1':

count = int(data['count'])

except BaseException as e:

print(e)

if (count>800)  :

print(url)

return count

# 切分矩形,把大矩形分成四个小矩形

def clipRectangle(r):

minlng = r[0]

minlat = r[1]

maxlng = r[2]

maxlat = r[3]

# dx = 0.5*(maxlng-minlng)

# dy = 0.5*(maxlat-minlat)

dx = 0.5*(maxlng-minlng)

dy = 0.5*(maxlat-minlat)

return [[minlng,minlat,minlng+dx,minlat+dy],[minlng+dx,minlat,maxlng,minlat+dy],

[minlng+dx,minlat+dy,maxlng,maxlat],[minlng,minlat+dy,minlng+dx,maxlat]]

# 按照步长,将面切分成矩形网格,把所有跟入参的面相交的矩形都返回

# polygon是多边形geometry,d是步长

def clipPolygon(polygon,d):

bbox = polygon.bounds

rectangles = []

lngnum = int((bbox[2]-bbox[0])/d)+1

latnum = int((bbox[3]-bbox[1])/d)+1

minlng = int(bbox[0]/d)*d

minlat = int(bbox[1]/d)*d

for i in range(0,lngnum+1):

for j in range(0,latnum+1):

rectangle = Polygon([(minlng+d*i,minlat+d*j),(minlng+d*i+d,minlat+d*j),

(minlng+d*i+d,minlat+d*j+d),(minlng+d*i,minlat+d*j+d),(minlng+d*i,minlat+d*j)])

if polygon.intersects(rectangle):

rectangles.append([minlng+d*i,minlat+d*j,minlng+d*i+d,minlat+d*j+d])

return rectangles

if __name__ =='__main__':

d = 0.03

#这个步长可以自己调整,大部分用的0.05

maxcount = 700

#这个就是设定网格返回POI数量最大值,大于这个值我们就认为这个网格需要拆分来获取更多精准点位

key = '你的key'

# 将成都的范围坐标点串从txt文件中读出,构建成polygon

# 成都的坐标点有多行,都要读取

# polygon这个包是构建多边形地图的

fpolyline = open(r'C:\\Users\\Administrator\\Desktop\\行政边界.txt','r',encoding='utf-8')

fnew = open(r'C:\\Users\\Administrator\\Desktop\\rectangles.txt','w+',encoding='utf-8',buffering=1)

for polyline in fpolyline.readlines():

polyline = polyline.strip()

polygonstr = 'POLYGON(('+polyline.replace(',',' ').replace(';',',')+'))'

print(polygonstr)

polygon = wkt.loads(polygonstr)

# 根据polygon和步长,构建矩形网格

rectangles = clipPolygon(polygon,d)

# 嵌入递归

for r in rectangles:

# 如果矩形构建的polygon与范围polygon不相交,进入下一个

rp = Polygon([(r[0],r[1]),(r[2],r[1]),(r[2],r[3]),(r[0],r[3]),(r[0],r[1])])

if not polygon.intersects(rp):

continue

count = countSearchPoi(key,r)

#print(count)

# 如果count小于阈值,将矩形和count写入文件,否则将矩形拆分,进行递归

if count < maxcount:

fnew.write(str(r[0])+','+str(r[1])+'|'+str(r[2])+','+str(r[3])+'\t'+str(count)+'\n')

else:

#print("我超过800啦")

rectangles.extend(clipRectangle(r))

fnew.close()

fpolyline.close()

这样一来,就会在桌面生成rectangles.txt这个文件,里面存储的就是拆分好了的网格和数量,我们就可以抓取这些网格里的数据并写入csv。

from asyncore import write

from sqlite3 import Timestamp

from time import time

import requests

from requests.adapters import HTTPAdapter

import math

import csv

import datetime

import pandas

s = requests.Session()

s.mount('http://', HTTPAdapter(max_retries=3))#设置重试次数为3次

s.mount('https://', HTTPAdapter(max_retries=3))

# 获取POI

# key,是高德地图的key

# r是矩形,minlng,minlat|maxlng,maxlat

# page是页码

def getPoi(key,r,page):

url = 'https://restapi.amap.com/v3/place/polygon?types=餐饮服务&offset=20&page='+str(page)+'&extensions=all&output=json&polygon='\

+ r + '&key=' + key

print(url)

result = []

try:

r = s.get(url, timeout=5)

data = r.json()

if 'status' in data:

if data['status'] == '1':

pois = data['pois']

for p in pois:

#按格子划分在边界可能采集到其他市数据,这里筛选掉

if (str(p['cityname']) == "成都市"):

result.append([str(p['name']),str(p['pname']),str(p['cityname']),str(p['adname']),str(p['address']),str(p['tel']),str(p['location']),str(p['id']),str(p['type']),str(p['typecode']),str(p['timestamp']),str(p['adcode']),str(p['business_area'])])

except BaseException as e:

print(e)

return result

if __name__ =='__main__':

key = '4e3e9cd261cc6dd7531938b00558d7d3'

f = open(r'C:\\Users\\Administrator\\Desktop\\rectangles.txt','r',encoding='utf-8')

# 1. 创建文件对象

filename = "C:\\Users\\Administrator\\Desktop\\抓取的高德POI_"+datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d_%H-%M-%S')+".csv"

#filename = "C:\\Users\\Administrator\\Desktop\\抓取的高德POI_2022-05-10_15-31-11.csv"

csvfile = open(filename,'a+',newline='',buffering=1,encoding='utf-8-sig')

writer = csv.writer(csvfile)

csvhead = ["名称","省份","城市","区县","详细地址","联系方式","经纬度","唯一ID","地点类型","地点类型代码","更新时间","所属区县代码","所属商圈"]

writer.writerow(csvhead)

flines = f.readlines()

f.close()

hang = 0

for l in flines:

print("已经完成"+(str)(hang)+"/"+str(len(flines)))

llist = l.strip('\n').split('\t')

r = llist[0]

count = int(llist[1])

hang = hang + 1

if count < 1:

continue

# 整除,向上取整

pagenum = math.ceil(count/20)

for i in range(1,pagenum+1):

result = getPoi(key,r,i)

if len(result) > 0:

for re in result:

#print(re)

# 2. 基于文件对象构建 csv写入对象

writer = csv.writer(csvfile)

# 3. 写入csv文件内容

writer.writerow(re)

#4. 关闭文件

csvfile.close()

#5. 去重,高德自己返回的数据里就有大量重复点位,需要用pandas的包去重

df = pandas.read_csv(filename)

df = df.drop_duplicates(subset='唯一ID', keep='last')

df.to_csv(filename)

这里代码层面就全部完成了,会在桌面到处一个csv如图:

这里面有几个需要注意的点:

1、因为行政区是不规则的多边形,按网格拆分在边缘部分必然会有其他城市区县的数据,所以在写入时就根据返回的城市参数做一次筛选,只写入成都的数据 if (str(p['cityname']) == "成都市"):

2、高德自己返回的数据里就有大量重复点位,需要用pandas的包去重

df = pandas.read_csv(filename)

df = df.drop_duplicates(subset='唯一ID', keep='last')

df.to_csv(filename)

这样大概就会得到一份15W条左右的成都餐饮服务POI点位数据,我截图里是另外处理过有18.5W条,据其他渠道了解全部数据大概是25W条。也就是说这样会大量缺失POI点位!

根据测试网上其他软件和数据,得到的成都餐饮POI大概都是15W,所以下面再讲高德的两个坑和改进思路。

第一个坑,返回的count是不正确的,实测count越大准确率越低,多次测试发现count最大值为70时候count准确度最高,这个问题提交工单反馈给高德,回复就是bug,哎就是不改,众所周知所谓“已经排期修改了”就是不改了。

所以如果要提高数据准确性和获取数量,代码中的maxcount最好设为70,但这样会浪费key的查询次数限额,个人做取舍吧。我设为70后得到的成都餐饮服务POI数据量18.3W

第二个坑,返回的POI点数不全!

例如我发现区域内某一个poi点位,在划定区域内不返回,但在另一个划定区域内有返回。这个点位都在这两个划定区域内,只不过两个划定区域起始结束经纬度不同。这个坑最大,查了半天最后发现是官方返回接口的问题。提工单咨询明确回复就是不给你全部数据,要全部数据别用这个接口,去找我们商务买。包括周边搜索这个接口测试一样的,都不会返回全。

所以这一块想拿到更多数据,技术上可行的方法只有多次用不同起始度的矩形划分抓取,得到多个导出结果最后合并去重,这样会浪费更多的key限额,比上一个key浪费都多。

尊敬的开发者:

您账号 下的工单有新回复。

多边形搜索API返回结果不准确

回复详情:
您好,由于考虑到数据安全问题,调用服务查询某类型的POI信息可能是无法返回全量数据的,搜索服务更推荐您输入关键字搜索进行精搜,若需要某类型的全量数据,可以提交商务工单咨询,给您带来不便,还请谅解~ 商务工单提交链接:高德控制台 有任何问题您都可以通过工单方式联系我们,感谢您的咨询与反馈,欢迎持续关注高德开放平台!工单完结可点击页面右上角的“关闭工单”,并对此次服务做出评价。

所以抓取高德、百度、腾讯地图最大的坑最后是官方不返回全部POI数据。要想获取全部的,我能想到的只有不同网格多次抓最后合并去重。不知道还有其他什么更好的高效省流方法。

不过如果不要求全量准确数据,损失三四成的数据也勉强够用了。

Python获取高德POI兴趣点数据 以及高德的两个坑 示例抓取成都市餐饮服务大类相关推荐

  1. python获取app信息的库_Python学习教程:另辟蹊径,appium抓取app应用数据了解一下...

    作为爬虫工程师,没有价格不知道selenium的. 什么是selenium? Selenium原本是一个用于Web应用程序自动化测试工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一 ...

  2. 获取高德POI兴趣点的详细信息

    获取高德POI兴趣点的详细信息 一,获取高德地图POI数据 第一步,申请"Web服务API"密钥(Key): 第二步,拼接HTTP请求URL,第一步申请的Key需作为必填参数一同发 ...

  3. 关于全国poi兴趣点数据

    1.POI数据介绍 POI是"Point of Interest"的缩写,中文可以翻译为"兴趣点".POI数据会包含各种信息,如前面提到的名称.别名等信息,可以 ...

  4. 使用Python获取股票单日成交数据

    本文主要介绍一个比较好的开源项目 Github:AKShare开源财经数据接口,该项目整合了各大财经网站的http接口:腾讯财经.新浪财经.网易财经.东财等....Github上介绍该项目是JetBr ...

  5. 可转债代码交流第二期:利用Python获取集思录数据(改)

    上期内容讲解了宁稳网数据获取的方法(用于初步计算未上市可转债价格),具体方法参考第一期:可转债代码交流第一期:利用Python获取宁稳网数据(包含基本的环境搭建与Python编辑器安装方法) 本人并非 ...

  6. python 获取东方财富网站的数据

    python 获取东方财富网站的数据 完整代码下载:https://github.com/tanjunchen/SpiderProject/tree/master/EastWealthWebsite ...

  7. 利用 Python 获取余额宝历史收益数据

    最近想做一个关于用一些指数基金与余额宝组成的简单 风险-无风险 投资组合的实验计算,发现通达信之类的行情软件并没有提供完整的余额宝收益信息,如通达信仅有年化收益率的数据,并没有万份收益的数据.因此考虑 ...

  8. Python获取丁香园疫情数据并解析json数据

    Python获取丁香园疫情数据并解析json数据 在利用pyecharts V1.x版本,使用Map对象进行制图时,对数据格式的要求比较高,如果数据格式不正确可能达不到我们预期的效果.在我的前一篇文章 ...

  9. c#使用正则表达式获取TR中的多个TD_[Python从零到壹] 四.网络爬虫之入门基础及正则表达式抓取博客案例...

    首先祝大家中秋节和国庆节快乐,欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都 ...

最新文章

  1. 携手320+合作伙伴,英伟达扔下一枚自动驾驶炸弹,打响新年越野赛 | CES2018
  2. OpenCASCADE绘制测试线束:数据交换命令之XDE 形状命令
  3. C++ STL:unordered_map::begin()函数不一定返回第一个元素
  4. UFLDL教程:Exercise:Vectorization
  5. 一键刷入twrp_小米/红米手机到手了该怎么解锁和刷 twrp
  6. 《IT蓝豹》PlayNewsStandDemo资讯类新闻客户端框架
  7. pythonnumpy详解_Python:Numpy详解
  8. 小熊派鸿蒙开发版,小熊派IoT开发板系列教程合集
  9. 获取包名列表android,更多获取app包名方法
  10. 我的Android进阶之旅------android中一些特殊字符(如:←↑→↓等箭头符号)的Unicode码值
  11. 树莓派python扫描蓝牙
  12. oracle增加字段为主键自增_Oracle新增自增一的主键字段和赋值代码
  13. Debezium报错处理系列之三十四:The db history topic or its content is fully or partially missing. Please check d
  14. 大数据与人工智能论文作业
  15. iPhone13ProMax和华为Mate50Pro区别 哪个好
  16. tp6+容联发送短信
  17. 支付宝花呗额度一直不涨?阿里老员工说出原因,亲测有效
  18. canvas粒子线条插件使用
  19. MySQL国内省市直辖区
  20. linux教程for语句,Bash 中的 For 循环详解

热门文章

  1. 带图,四阶魔方还原的特殊情况,顶层中间对互换,顶层翻棱,五阶魔方拼棱的特殊情况
  2. 数控车床加工螺纹时常见故障及解决方法
  3. ElasticSearch实战踩坑记录
  4. 分手,才是幸福的开始
  5. python樱花树代码_自动生成樱花树
  6. Quicken 2009 The Official Guide
  7. Debian 6.0安装igb驱动
  8. Ubuntu系统下Clion、Visual Studio Code安装和使用教程|1-7
  9. ef mysql自动更新_EntityFrameworkCore使用Migrations自动更新数据库
  10. win10安装账户卡住_Win10安装出现卡死或黑屏怎么办?Win10安装卡死或黑屏问题解决方法...