前端(echarts+百度地图api)后端(python+flask)完成绵阳餐饮消费数据可视化系统

您所在的位置:网站首页 餐饮类网站设计 前端(echarts+百度地图api)后端(python+flask)完成绵阳餐饮消费数据可视化系统

前端(echarts+百度地图api)后端(python+flask)完成绵阳餐饮消费数据可视化系统

2024-06-29 11:39| 来源: 网络整理| 查看: 265

一、可视化要求

针对团购网站餐饮类消费数据的可视分析系统设计与开发 大多数消费者在选择团购网站消费后会做出相应评价,从而产生海量的交易数据。这些数据包含了消费者对饮食比较全面的主观性评价和量化评分,因此通过对此类餐饮数据的分析能够有效洞悉城市餐饮消费行为。而由于该类数据体量大、数据类型多等特点,使得传统的数据分析技术已经难以有效进行分析处理。如何将可视分析技术应用于团购网站餐饮类数据分析,探索城市消费行为是一个新颖的研究课题。提供某团购网站绵阳市3444家餐饮类店铺数据的基本信息及351941条评论信息,其中164982位用户参与评论。

设计开发针对团购网站餐饮类消费数据的可视分析系统,实现功能包括但不限如下: (1)呈现城市餐饮店铺时空特征分布和热门店铺特色美食; (2)挖掘城市餐饮消费行为的地域特征倾向和时序特征,店铺消费关联关系分析; (3)支持针对自定义消费条件的个性化推荐;

二、待分析数据 店铺信息shop_detail.csv

在这里插入图片描述 字段 店铺ID 店铺名称 平均得分 地址 电话 营业时间 其他信息 经纬度 平均价格 品牌ID 品牌名称 展示状态 安全档案 店铺标签

用户评论信息shop_comment.csv

在这里插入图片描述 字段 用户ID 平均价格 评论 图片URL 评论时间 点赞数 用户名 店铺评分 评论ID 是否匿名 店铺ID 用户等级

三、数据预处理

第一个功能: 呈现城市餐饮店铺时空特征分布和热门店铺特色美食

呈现店铺时空特征分布我选择使用百度地图来实现,采用这种方式来实现地图比较方便,对于店铺的地理位置绘制、店铺基本信息展示、店铺所属区域绘制、路线规划都能够做到,相比于简单的echarts地图更加强大。

热门店铺美食在shop_details.csv文件中并没有,所以只有自己来爬取数据。

百度地图api用的是BD09经纬度坐标,要想得到BD09坐标需要使用百度api来对美团上爬取的经纬度坐标进行转换(正好可以在爬取数据时对坐标系进行转换)

美团的移动端(https://i.meituan.com/)爬取相对容易,但是店铺的信息比较少

所以还是在桌面端爬取数据https://my.meituan.com/

店铺详情页面的url通过拼接店铺id获取(https://my.meituan.com/meishi/4615402),现在就是直接访问。就是一个简单的get请求,但是要带上完整的cookie,cookie有问题的话很快会弹验证码。一个cookie可以爬1000次后才会出现验证码,但是也有几百次出现的。我是通过手动更换cookie和ip来爬取的数据。 具体想解决该问题可以参考:https://blog.csdn.net/xing851483876/article/details/81842329

爬虫spider.py

# -*- coding: utf-8 -*- # @Author : f # @File : spider.py import requests import pandas as pd import re import csv import json import util import time ''' 根据店铺id获取特色美食(名称、图片地址)、店铺分类 并保存到csv文件中 ''' comment_data_path = ".\static\data\shop_comments.csv" shop_data_path = ".\static\data\shop_details.csv" comment_df = pd.read_csv(comment_data_path) shop_df = pd.read_csv(shop_data_path) # 获取店铺的id列表 def get_id(): return list(shop_df['poiId']) def get_detail_byId(id): ''' :param list_id: 店铺id :return: 店铺信息 ''' base_url = "https://my.meituan.com/meishi/" open_url = base_url+str(id) headers = { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Connection': 'keep-alive', 'Host': 'my.meituan.com', 'Referer': 'https://gz.meituan.com/meishi/', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36', "Cookie":"_lxsdk_cuid=176a76e5465c8-08c7fda338591a-366b4108-144000-176a76e5466c8; _hc.v=47584a75-cd95-5d49-4774-f90292228934.1609128422; iuuid=DE08D7129F1C9300F442BD3F534FE1E9BEB5960DC89FC964B06DE75D92AF59FF; _lxsdk=DE08D7129F1C9300F442BD3F534FE1E9BEB5960DC89FC964B06DE75D92AF59FF; webp=1; __utma=74597006.1600449216.1609384800.1609384800.1609384800.1; __utmz=74597006.1609384800.1.1.utmcsr=link.csdn.net|utmccn=(referral)|utmcmd=referral|utmcct=/; ci=306; cityname=%E7%BB%B5%E9%98%B3; latlng=31.540156,104.689433,1609384831294; i_extend=C019032296837928515275757042931456002187_c14_e76093ef0e7669cc9c26a543f38b45487GimthomepageguessH__a; lsu=; __mta=221682599.1609128395200.1609636290109.1609637806937.14; client-id=aff308ba-8320-4796-8a4c-1303c58c5136; uuid=099f6650-8b35-423a-a6ea-3d226bb37422; _lx_utm=utm_source%3Dlink.csdn.net%26utm_medium%3Dreferral%26utm_content%3D%252F; lat=31.504214; lng=104.784832; _lxsdk_s=176ccfd3748-f1a-79a-0bb%7C%7C2" } res = requests.get(open_url,headers=headers) res.encoding = "utf-8" l = [id] # 存储店铺信息的列表 pattern = "window._appState = (.*?);" rec = re.compile(pattern) # 预编译 if rec.search(res.text): json_str = rec.search(res.text).groups() for j in json_str: d = json.loads(j) l.append(d['detailInfo']['name']) l.append(d['crumbNav']) l.append(d['recommended']) l.append(d['detailInfo']['avgScore']) l.append(d['detailInfo']['address']) l.append(d['detailInfo']['phone']) l.append(d['detailInfo']['openTime']) l.append(d['detailInfo']['avgPrice']) result = util.wgs84tobd09(d['detailInfo']['longitude'],d['detailInfo']['latitude']) l.append(result[0]['x']) l.append(result[0]['y']) return l #保存店铺信息 def save_info(id_list): ''' :param id_list: 店铺id列表 :return: ''' with open("../static/data/shop_details02.csv","a+",encoding="utf-8",newline="") as f: # 2. 基于文件对象构建 csv写入对象 csv_writer = csv.writer(f) # 3. 构建列表头 csv_writer.writerow(["poiId", "name", "type","recommended","avgScore","address","phone","openTime","avgPrice","longitude","latitude"],newline="") # 4. 写入csv文件内容 for id in id_list: info_list = get_detail_byId(id) print(info_list) csv_writer.writerow(info_list) def main(): print("------{} 开始爬取数据------".format(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))) id_list = get_id() save_info(id_list) print("------{} 爬取数据结束------".format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))) if __name__ == '__main__': main()

爬取结果文件shop_detail02.csv 在这里插入图片描述

四、数据处理

1)、店铺类型 获取到的数据是页面面包屑导航的数据,类似于 [{‘title’: ‘绵阳美团’, ‘url’: ‘http://my.meituan.com/’}, {‘title’: ‘绵阳美食’, ‘url’: ‘http://my.meituan.com/meishi/’}, {‘title’: ‘绵阳火锅’, ‘url’: ‘http://my.meituan.com/meishi/c17/’}] 需要的数据就是三级导航中的文本数据 基本数据格式都是地名+店铺类型 所以我对店铺种类进行了处理

# 处理店铺类别 def convert_shop_type(type_str): str = eval(type_str)[2]['title'][2:] if str[0:1] == "县" or str[0:1] == "区": return str[1:] return str

2)、店铺推荐菜 对于推荐菜的处理比较简单,爬取的推荐菜数据都有几十条,只取其中热门前三条

3)、店铺经纬度 百度地图api用的是BD09经纬度坐标 要想得到BD09坐标需要使用百度api来对美团上爬取的经纬度坐标进行转换

# 坐标系转换 def wgs84tobd09(lon,lat): api_url = "http://api.map.baidu.com/geoconv/v1/?coords={},{}&from=1&to=5&ak=k4NUsxZb6DuuOxQOoZqneCKRPp3St76v".format(lon,lat) res = requests.get(api_url) d = json.loads(res.text) return d["result"]

4)、店铺营业时间 对于店铺营业时间我只取了一个时间段,而且视作无休 只考虑一整天的营业时间

营业时间的格式类似于 周一至周日 10:00-13:00 16:00-21:30 由于每家店的营业时间格式都不太同,所以我就提取了其中的一段 比如上面这条数据就只提取了10:00-13:00 并由-把时间段分隔开分别进行处理 比如10:00的处理 106+0/10=60 13:00的处理 136+0/10=78 那么处理数据后店铺字段commentTime_convert={ “start”:60, “end”:78 } 前端通过控制datazoom来控制整个地图的时间轴,时间轴的两边是1-100 但是数据的显示跨度是144(按照0:00到24:00 每10分钟作一个刻度,所以上面的数据处理就是根据这来的) 当datazoom改变时分别将开始的值和结束的值乘以1.44 然后在遍历地图所有的点进行一一对比筛选出符合营业条件的点

处理代码: 后端:

# 处理店铺营业时间 def convert_openTime(openTime): start = 0 end = 0 if isinstance(openTime, str): l = openTime.split(" ") if len(l) > 1: if l[1] == "全天" or l[1] == "周一至周日" or l[1] == "周五至周日": start = 0 end = 144 else: start_str = l[1].split("-")[0] end_str = l[1].split("-")[1] start = int(start_str.split(":")[0]) * 6 + int(start_str.split(":")[1]) / 10 end = int(end_str.split(":")[0]) * 6 + int(end_str.split(":")[1]) / 10 else: start = 0 end = 144 return {"start":start,"end":end}

前端:

myChart.on('dataZoom', function(e) { // console.log(e); // All params time_start = e.start * 1.44; time_end = e.end * 1.44; time_mapPoints = []; for(var i = 0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3