重磅出击!第一站,新鲜出炉,1024特供,手把手教你抓取高德地图搜索结果,获取全国海底捕捞信息并实现可视化
高德地图是我们经常用的地图软件,今天我们以海底捞为例子,取全国的海底捞搜索结果为自己用!这里的方法只用于个人采集公开数据,请不要用于商业或违法用途!
因为整个C站还没有分享这方面技术的,所以我写一篇给大家节日助助兴!喜欢的大哥大姐们给我点点关注点点赞~
项目公开地址:
https://codechina.****.net/s*2/haidilao.git
可以直接clone到本地
工具
编程软件:Pycharm
Python版本:3.7.6
浏览器:双核浏览器
依赖库:urllib,requests,json,pymysql
分析工具:Postman
分析网页获取第一个接口
首先进入高德地图,搜索范围地选全国,然后F12检查,进入网络(network)界面,在搜索框输入‘海底捞’,先不点搜索按钮,清空网络界面的数据,再点击,此时的界面一般是这样的,
逐个点击右侧像数据接口的网页,发现第一个下划线的东西,返回的数据中存有左侧搜索结果的城市信息
看直辖市上海,城市名和搜索结果数都能对应,很显然,这将是我们取得的第一层数据。
根据生活经验,对照上张图片的搜索结果,容易得出,areacode是地区电话号码的区号,ename是地区英文名称,adcode是邮编,total是地区的海底捞搜索结果数,name是地区中文名称
由于这里的数据有200多条,后续的数据更多,所以这里建立数据库存储,把这个数据存放在city表
创建第一个数据库表
city表的创建语句
sql1='''
CREATE TABLE IF NOT EXISTS `city` (
`adcode` VARCHAR(6) NOT NULL DEFAULT '邮政编码' COMMENT '邮政编码',
`areacode` VARCHAR(6) NOT NULL DEFAULT '电话区号' COMMENT '电话区号',
`name` VARCHAR(45) NOT NULL DEFAULT '城市名称' COMMENT '城市名称',
`ename` VARCHAR(45) NOT NULL DEFAULT '城市英文名称' COMMENT '城市英文名称',
`total` INT NOT NULL DEFAULT 0 COMMENT '数量',
PRIMARY KEY (`adcode`))
ENGINE = InnoDB
COMMENT = '城市信息';
'''
postman测试接口的请求参数
通过postman测试这个接口要什么请求头参数才能获得
直接获取显然是不行的
再尝试发现加上UA和cookies就可以了
保存接口响应的数据
直接用postman生成python代码,取得这个数据保存到文件里,或者直接复制postman下面的数据保存到文件里即可
import requests
url = "https://www.amap.com/service/poiInfo?query_type=TQUERY&pagesize=20&pagenum=1&qii=true&cluster_state=5&need_utd=true&utd_sceneid=1000&div=PC1000&addr_poi_merge=true&is_classify=true&zoom=4&city=100000&geoobj=109.337074%7C22.883865%7C151.524574%7C55.107505&keywords=%E6%B5%B7%E5%BA%95%E6%8D%9E"
payload={}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
'Cookie': 'cna=oAf1GYRz1i8CAd9CwvtddGMZ; UM_distinctid=17ca72d57e1483-0f1c7a9cb0dbbe-3e604809-100200-17ca72d57e253e; _uab_collina=163489234130013489187538; xlly_s=1; passport_login=NzQ0NzcwNTc0LGFtYXBfMTc3MDE3MjA2MTNDanEzRG9SV3IsYWVsdzZqcWI0N3dmcXFieDI3dXBjZmh5aW5icGtvd3ksMTYzNDk4MTMzOCxOMlF6WVdFd01XWXpPV0UxWVdVMU5EQmtZMk5pWldVelpEZzRNREJtTkdZPQ%3D%3D; guid=0779-0b27-044d-88f0; CNZZDATA1255626299=1457747375-1634883830-https%253A%252F%252Fwww.baidu.com%252F%7C1635035171; x5sec=7b227761676272696467652d616c69626162612d616d61703b32223a223863376433376639643564383833336662363934656531653864333364303739434f2f73306f7347454c75387434443132715767677745773370656271514d3d227d; x-csrf-token=5cb578d04186b6ce4edbf05f5f27156d; tfstk=cqkGBg6vCfP_rPNnNdwsAeg-xNAGaqIa57FELYxRLiq8VHDUbsAVT_rJ8NaF4N5f.; l=eBauGa7ng1lUhZ5MBO5aourza779oIRfG-VzaNbMiInca6M1vFNCXNCLyI8kBdtjgtCfhUxPkdi_7RHBJBzdg5n8-lA1tTf7NxJO.; isg=BAUFUVadyPDFXOyqDS4SiIyzFEE_wrlUzh-b8QdqRTxLniEQxRIrJLs4qMJo5NEM'
}
response = requests.request("GET", url, headers=headers, data=payload)
with open('citydata.txt','w') as f:
print(response.text)
f.write(response.text)
将响应数据存入数据库
用json读取这个文件,取得第一层城市信息数据,存入数据库中
import json
with open('citydata.txt','r') as f:
jsondata=json.load(f)
regions=jsondata.get('data').get('suggestion').get('regions')
for region in regions:
sql2=f"""insert into `city` (`areacode`,`adcode`,`name`,`ename`,`total`) value ('{region['areacode']}','{region['adcode']}','{region['name']}',"{region['ename']}",'{region['total']}');"""
try:
rows=cursor.execute(sql2)
except:
print(sql2+"\n语句执行失败")
continue
client.commit()
分析网页获取第二类接口
现在我们已经取得了第一张表,接下来查看城市里面的结果是如何显示的,从左侧城市结果里面点击广东进入广州市,还是找新的接口,和前面的接口名字差不多
但是广州的数据不止这界面的20条,20条是被查询参数限制的,所以要修改查询参数
还是通过postman做尝试,一旦postman尝试出错就要跑到浏览器里面重新打开高德进行滑块验证
分析网址
https://www.amap.com/service/poiInfo?query_type=TQUERY&pagesize=20&pagenum=1&qii=true&cluster_state=5&need_utd=true&utd_sceneid=1000&div=PC1000&addr_poi_merge=true&is_classify=true&zoom=9.34&city=320000&geoobj=118.287654%7C31.470088%7C119.325966%7C32.364258&keywords=%E6%B5%B7%E5%BA%95%E6%8D%9E
这是浏览器中取得的南京地区的搜索结果接口地址,里面参数很多
对比广州,utd_sceneid参数是不一样的,这个参数值并不清楚怎么取得,尝试修改参数或者去掉这个参数,发现是可以的
放到postman里面,改need_utd 为false,去掉utd_sceneid参数,还是可以正常获取
这里发现南京的city是320000,但数据库存的是320100,这样没法自动化,尝试修改看看。
发现还是可以正常获取数据的
接着把经纬度geoobj去掉看看
结果顺序好像是不一样的,内容没问题
剩下的可能只要把pagesize改为总数即可获取全部
现在的半纯净地址是
https://www.amap.com/service/poiInfo?query_type=TQUERY&qii=true&cluster_state=5&need_utd=false&div=PC1000&addr_poi_merge=true&is_classify=true&zoom=4&city=320100&keywords=%E6%B5%B7%E5%BA%95%E6%8D%9E&pagesize=20&pagenum=1
提取里面的pagesize和city作为我们要调整的循环变量,在数据库中读出
## 数据库读取关键参数
sql3='select adcode,total from `city`'
cursor.execute(sql3)
data=cursor.fetchall()
# print(data)-》》(('110000', 255), ('120000', 122), ('130100', 31),
## 数据库关键参数读取结束
经过测试,一次性读取的最大值是30,给他再打的pagesize都不会改变一个页面最多30条,所以要分页多次读取,为了伪装,我们还是选用默认的20个作为参数,调整分页页面的值
获取数据,存到文件
读取过程中,可以发现,其实是没有第一层搜索里显示的这么多数据的,搜索结果内部的比如北京255,保存的数据只有6页,120多个,网页上点进去其实也是这个结果,获取的数据量应该还算完整
又比如在这个地区的搜索框是有一个海底捞结果的,但是这个店本身也不是海底捞,在查询接口时会被过滤掉,所以第一层里面的存储total其实不起作用。把程序中的分页逻辑调整为检查数据中是不是包含“未找到”。
Cookie失效的处理
基本上一个新的cookie用到一半左右,会出现错误,要自己去网页端验证一下,拿到新的cookie,这可能是服务器存的数据改变引起的,cookie本身的时间是很长的,最早过期的都要到第二天。
请求各城市数据接口的代码
# ## 从网页读取各城市数据
#
headers = {
'Cookie': 'cna=oAf1GYRz1i8CAd9CwvtddGMZ; UM_distinctid=17ca72d57e1483-0f1c7a9cb0dbbe-3e604809-100200-17ca72d57e253e; _uab_collina=163489234130013489187538; xlly_s=1; passport_login=NzQ0NzcwNTc0LGFtYXBfMTc3MDE3MjA2MTNDanEzRG9SV3IsYWVsdzZqcWI0N3dmcXFieDI3dXBjZmh5aW5icGtvd3ksMTYzNDk4MTMzOCxOMlF6WVdFd01XWXpPV0UxWVdVMU5EQmtZMk5pWldVelpEZzRNREJtTkdZPQ%3D%3D; guid=f0d3-cf05-bf77-9378; CNZZDATA1255626299=1457747375-1634883830-https%253A%252F%252Fwww.baidu.com%252F%7C1635045974; x-csrf-token=f4ebc0f94d2b80fe0fb5220728c910e1; x5sec=7b227761676272696467652d616c69626162612d616d61703b32223a223035353835376531353438323533353339643038343163336438373463643961434b544e303473474549726f714d4c7737347939667a44656c35757041773d3d227d; tfstk=c7_1BNiC1AD1Pbrq71NE3UdWCsvcaT3Bt2vF1i1x2Jbsf5fwpsDjzLZDkCMCcKAC.; l=eBauGa7ng1lUhlR3BO5Zlurza77OiIdf5CVzaNbMiInca6gGYFtjBNCLytP9QdtxgtCfWe-Pkdi_7RFyJFUdg5n8-lA1tTf7TxJO.; isg=BFtbTWMKvpa_scLkR_wEblah6r_FMG8ypC01v02ZutpxLHIO1gBGgmfqx4yiI8cq',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
}
error_msg = '哎哟喂,被挤爆啦,请稍后重试'
end_msg ='未找到'
for i in data:
total = 1
adcode = i[0]
while True:
status = 200
url=f'https://www.amap.com/service/poiInfo?query_type=TQUERY&qii=true&cluster_state=5&need_utd=false&div=PC1000&addr_poi_merge=true&is_classify=true&zoom=4&city={adcode}&keywords=%E6%B5%B7%E5%BA%95%E6%8D%9E&pagesize=20&pagenum={total}'
print(url)
# exit()
response = requests.request("GET", url, headers=headers).text
## 不要造成密集请求被屏蔽
time.sleep(2)
if error_msg in response:
status=400
while status!=200:
headers['Cookie']=input('出现错误,请自行寻找cookie输入')
response = requests.request("GET", url, headers=headers).text
if error_msg in response:
status = 400
else:
status=200
if end_msg in response:
break
filename='data/'+adcode+f'-{total}'
print(filename)
with open(filename,'w',encoding='utf-8') as f:
f.write(response)
total+=1
## 城市数据写入完成
分析接口数据格式
每个店的参数在浏览器看到的有这些
我们从接口得到的也有许多前端没有渲染的数据
综合一下,我们根据文件里的名称提取以下数据
districtcode-地址的邮编,address-地址,provincename-省份,districtname-区名,cityname-城市名称,name-店名,id-元素的id,latitude-纬度,longitude-经度,src_star-评分,review_total-浏览量,tel-电话,business_area-商业区,opentime-开闭时间,averagecost-人均消费,pcdr-精确到路的地址,tag_display-菜品
同时在数据库表中保存城市的adcode,作为外键
建数据表语句
## 建数据表语句
createtable2="""
CREATE TABLE IF NOT EXISTS `result` (
`id` varchar(10) NOT NULL COMMENT "网页id",
`districtcode` varchar(6) NOT NULL DEFAULT "邮编" COMMENT "具体的邮编",
`address` varchar(150) NOT NULL DEFAULT "地址" COMMENT "地址",
`provincename` varchar(20) NOT NULL DEFAULT "省份名称" COMMENT "省份名称",
`districtname` varchar(20) NOT NULL DEFAULT "区域名称" COMMENT "区域名称",
`cityname` varchar(30) NOT NULL DEFAULT "城市名称" COMMENT "城市名称",
`name` varchar(150) NOT NULL DEFAULT "店名" COMMENT "地址名称",
`latitude` double NOT NULL DEFAULT "0" COMMENT "纬度",
`longitude` double NOT NULL DEFAULT "0" COMMENT "经度",
`src_star` double NOT NULL DEFAULT "0" COMMENT "评分",
`review_total` INT NOT NULL DEFAULT "0" COMMENT "访问量",
`tel` varchar(200) NOT NULL DEFAULT "电话号码" COMMENT "电话",
`business_area` varchar(45) NOT NULL DEFAULT "商业区",
`opentime` varchar(45) NOT NULL DEFAULT "开闭时间" COMMENT "开闭时间",
`averagecost` float NOT NULL DEFAULT "0" COMMENT "人均",
`pcdr` varchar(45) NOT NULL DEFAULT "精确到路的地址" COMMENT "精确到路的地址",
`tag_display` varchar(250) DEFAULT "菜品" COMMENT "菜品",
`adcode` varchar(6) NOT NULL DEFAULT "区域代码" COMMENT "区域代码",
PRIMARY KEY (`id`),
KEY `adcode_idx` (`adcode`),
CONSTRAINT `adcode` FOREIGN KEY (`adcode`) REFERENCES `city` (`adcode`) ON DELETE CASCADE
)ENGINE = InnoDB
COMMENT="搜索结果";
"""
cursor.execute(createtable2)
client.commit()
## 建数据表完成
提取关键字到数据库
提取文件数据到数据库中,这里提取过程中输出了数据,可以在出问题时看看是什么样的数据出问题了,对数据库和语句做调整
我这里修改了几次字段的大小之后,成功执行完毕
## 转换城市数据到数据库
dirlist=os.listdir('data')
for filename in dirlist:
with open('data/'+filename,'r',encoding='utf-8') as f:
jsondata=json.load(f)
shuju=jsondata.get('data').get('poi_list')
for table in shuju:
id=table.get('id')
districtcode=table.get('districtcode')
address=table.get('address')
provincename=table.get('provincename')
districtname=table.get('districtname')
cityname=table.get('cityname')
name=table.get('name')
latitude=table.get('latitude')
if not latitude:
latitude=0
longitude=table.get('longitude')
if not longitude:
longitude=0
src_star=table.get('src_star')
if not src_star:
src_star=0
review_total=table.get('review_total')
if not review_total:
review_total=0
tel=table.get('tel')
business_area=table.get('business_area')
opentime=table.get('opentime')
averagecost=table.get('averagecost')
if not averagecost:
averagecost=0
try:
int(averagecost)
except:
print(averagecost)
averagecost=0
pcdr=table.get('pcdr')
tag_display=table.get('tag_display')
adcode=filename.split('-')[0]
print(id, districtcode, address, provincename, districtname, cityname, name, latitude,
longitude, src_star, review_total, tel, business_area, opentime, averagecost,
pcdr, tag_display, adcode)
sql4=f"insert into `result` (`id`, `districtcode`, `address`, `provincename`, `districtname`, `cityname`, `name`, `latitude`,`longitude`, `src_star`, \
`review_total`, `tel`, `business_area`, `opentime`, `averagecost`, `pcdr`, `tag_display`, `adcode`) value ('{id}', '{districtcode}','{address}',\
'{provincename}','{districtname}', '{cityname}', '{name}', '{latitude}', '{longitude}', '{src_star}', \
'{review_total}', '{tel}', '{business_area}', '{opentime}', '{averagecost}', '{pcdr}', \
'{tag_display}', '{adcode}')"
rows=cursor.execute(sql4)
print(rows)
client.commit()
结果
在全国共获取有效的海底捞店铺1954家!
数据的存储效果
拓展
这次我们查的是海底捞,每个城市不一定有海底捞,但一定都有学校、医院这种公共设施,这次的搜索结果表明第一层数据的total数据是没用的,所以我们完全可以直接从第二个链接开始。首先做一个数据表存储所有的城市邮编,然后就可以挨个检索。
海底捞是keywords参数
keywords=%E6%B5%B7%E5%BA%95%E6%8D%9E
后面的字段可以通过urllib的parse包的quote方法转换中文为这个字符串
据此可以搜索任意想要的关键字的结果。
文章到这里还没结束,我再拓展拓展可视化的操作,大家先给我点点赞,收藏收藏,鼓励鼓励
Powerbi的分析
工具
这里选用windows的powerbi做分析,版本就最新版,刚下载的还热乎着。如果你是windows7及以下版本,那你下载的时候看最后支持的版本,windows7在今年年初就不支持了,其他的更早。
数据表的读取直接从数据库连接即可,选更多,找到mysql,这里可能需要先安装一个mysql-connector-net-8.0.20.msi,如果你找不到,请留言,我给你发下载地址。
这里用到的功能并不复杂,低版本可能只是操作的位置不一样,其他都一样。
先看哪个省的海底捞最多
建立一个度量值
店铺数量 = COUNT(‘db2 result’[name])
在报表界面,建立一个矩阵,行依次放置省份名称,城市名称,街道名称,值放置店铺数量
展开的显示效果为
这是经过排序的,可以发现,广东省最多
去掉省,排序可以发现,上海最多,深圳市是排第一的 一个市级地区
以省级生成环形图看店铺的大致分布情况
看哪个地方搜索损失率最大
在市级层面可以看得到的结果和最初的搜索结果数的比率
新建度量值
搜索数量 = SUM(‘db2 city’[total])
损失率 = 1-[店铺数量]/[搜索数量]
这里需要将对应关系切换为city表name对应result表的cityname,外键关系切换为虚线,这个关系为实线
建一个表格,第一个name取的是city表的
可以看到有些地方直接就是没有的
如果要看省一级的,需要对数据进一步拆分,将省市区的对应关系单独建表
更多的视图自己探索啦~
上一篇: 如何成为一名出色的海底毛巾清洁工
下一篇: 海迪劳商业模式分析--管理模式