Python获取高德POI兴趣点数据 以及高德的两个坑 示例抓取成都市餐饮服务大类
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兴趣点数据 以及高德的两个坑 示例抓取成都市餐饮服务大类相关推荐
- python获取app信息的库_Python学习教程:另辟蹊径,appium抓取app应用数据了解一下...
作为爬虫工程师,没有价格不知道selenium的. 什么是selenium? Selenium原本是一个用于Web应用程序自动化测试工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一 ...
- 获取高德POI兴趣点的详细信息
获取高德POI兴趣点的详细信息 一,获取高德地图POI数据 第一步,申请"Web服务API"密钥(Key): 第二步,拼接HTTP请求URL,第一步申请的Key需作为必填参数一同发 ...
- 关于全国poi兴趣点数据
1.POI数据介绍 POI是"Point of Interest"的缩写,中文可以翻译为"兴趣点".POI数据会包含各种信息,如前面提到的名称.别名等信息,可以 ...
- 使用Python获取股票单日成交数据
本文主要介绍一个比较好的开源项目 Github:AKShare开源财经数据接口,该项目整合了各大财经网站的http接口:腾讯财经.新浪财经.网易财经.东财等....Github上介绍该项目是JetBr ...
- 可转债代码交流第二期:利用Python获取集思录数据(改)
上期内容讲解了宁稳网数据获取的方法(用于初步计算未上市可转债价格),具体方法参考第一期:可转债代码交流第一期:利用Python获取宁稳网数据(包含基本的环境搭建与Python编辑器安装方法) 本人并非 ...
- python 获取东方财富网站的数据
python 获取东方财富网站的数据 完整代码下载:https://github.com/tanjunchen/SpiderProject/tree/master/EastWealthWebsite ...
- 利用 Python 获取余额宝历史收益数据
最近想做一个关于用一些指数基金与余额宝组成的简单 风险-无风险 投资组合的实验计算,发现通达信之类的行情软件并没有提供完整的余额宝收益信息,如通达信仅有年化收益率的数据,并没有万份收益的数据.因此考虑 ...
- Python获取丁香园疫情数据并解析json数据
Python获取丁香园疫情数据并解析json数据 在利用pyecharts V1.x版本,使用Map对象进行制图时,对数据格式的要求比较高,如果数据格式不正确可能达不到我们预期的效果.在我的前一篇文章 ...
- c#使用正则表达式获取TR中的多个TD_[Python从零到壹] 四.网络爬虫之入门基础及正则表达式抓取博客案例...
首先祝大家中秋节和国庆节快乐,欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都 ...
最新文章
- 携手320+合作伙伴,英伟达扔下一枚自动驾驶炸弹,打响新年越野赛 | CES2018
- OpenCASCADE绘制测试线束:数据交换命令之XDE 形状命令
- C++ STL:unordered_map::begin()函数不一定返回第一个元素
- UFLDL教程:Exercise:Vectorization
- 一键刷入twrp_小米/红米手机到手了该怎么解锁和刷 twrp
- 《IT蓝豹》PlayNewsStandDemo资讯类新闻客户端框架
- pythonnumpy详解_Python:Numpy详解
- 小熊派鸿蒙开发版,小熊派IoT开发板系列教程合集
- 获取包名列表android,更多获取app包名方法
- 我的Android进阶之旅------android中一些特殊字符(如:←↑→↓等箭头符号)的Unicode码值
- 树莓派python扫描蓝牙
- oracle增加字段为主键自增_Oracle新增自增一的主键字段和赋值代码
- Debezium报错处理系列之三十四:The db history topic or its content is fully or partially missing. Please check d
- 大数据与人工智能论文作业
- iPhone13ProMax和华为Mate50Pro区别 哪个好
- tp6+容联发送短信
- 支付宝花呗额度一直不涨?阿里老员工说出原因,亲测有效
- canvas粒子线条插件使用
- MySQL国内省市直辖区
- linux教程for语句,Bash 中的 For 循环详解
热门文章
- 带图,四阶魔方还原的特殊情况,顶层中间对互换,顶层翻棱,五阶魔方拼棱的特殊情况
- 数控车床加工螺纹时常见故障及解决方法
- ElasticSearch实战踩坑记录
- 分手,才是幸福的开始
- python樱花树代码_自动生成樱花树
- Quicken 2009 The Official Guide
- Debian 6.0安装igb驱动
- Ubuntu系统下Clion、Visual Studio Code安装和使用教程|1-7
- ef mysql自动更新_EntityFrameworkCore使用Migrations自动更新数据库
- win10安装账户卡住_Win10安装出现卡死或黑屏怎么办?Win10安装卡死或黑屏问题解决方法...