背景
在旅游行业中,了解游客对旅游景点的评论和评价对于景点管理和市场营销至关重要。通过采集旅游景点评论数据并进行可视化分析,可以帮助景点管理者更好地了解游客对景点的看法和体验,发现优劣势,优化服务和提升用户满意度。基于Python Flask框架,搭建旅游景点评论数据可视化大屏,不仅可以展示评论情感分析结果、关键词词频分布,还能通过图表展示不同景点的评分趋势、热门景点排名等信息,为景点管理者提供直观的数据分析和决策支持。这种基于Python Flask的旅游景点评论数据可视化大屏实现,可以帮助景点管理者及时了解用户反馈,优化景点运营和服务质量,提升景点竞争力和吸引力。这一研究背景旨在通过数据驱动的方法,提升旅游景点管理水平,提供更好的旅游体验,促进旅游业的可持续发展。
技术栈
flask框架
html+js+css
mysql8.0
pandas
echarts可视化
requests爬虫
前端设计
页面结构:
页面采用HTML5标准,包含了<!doctype html>声明,<html>、<head>和<body>等标签。
页面主要内容集中在<body>标签内,包括头部(header)和主要内容区域。
样式和脚本:
在<head>标签内引入了jQuery库和各种ECharts相关的JavaScript文件,用于数据可视化的图表展示。
引入了CSS样式表和自定义的JavaScript文件,用于页面的样式设置和交互效果的实现。
动态元素:
页面包含动态的粒子效果图(canvas)和加载动画,为用户呈现视觉效果。
使用iframe嵌入外部页面(index.html)以展示动态内容。
数据展示:
页面主要分为三栏布局,每栏内包含不同的数据可视化图表(如柱状图、饼图、词云图等)和数据展示区域。
使用ECharts库实现数据可视化,通过Ajax请求获取后端数据,并动态更新图表内容。
实时刷新:
使用JavaScript定时函数(setInterval)定时刷新页面数据,保持数据的实时性。
不同图表的刷新频率可能有所不同,以确保数据的及时更新和展示。
响应式设计:
页面部分元素设置了固定高度,适应不同屏幕尺寸的展示需求。
图表和内容区域的布局灵活,以适配不同的显示设备。
flask实现主要代码:
class CorpData(SourceDataDemo):
def __init__(self):
"""
按照 SourceDataDemo 的格式覆盖数据即可
"""
super().__init__()
self.title = '旅游景点评论数据采集与可视化大屏'
self.counter = {'name': '景点数', 'value': tj()[0]}
self.counter2 = {'name': '评论数', 'value': tj()[1]}
self.echart1_data = {
'title': '套餐类型分析',
'data': pinpai()
}
self.echart2_data = {
'title': '不同景点评论数',
'data': jiage()
}
self.echarts3_1_data = {
'title': '是否VIP分析',
'data': cpu_1()
}
self.echart4_data = {
'title': '不同年份评论数对比',
'data': [
{"name": "数量", "value":xiaoliang()['数量']},
],
'xAxis': xiaoliang()['年'],
}
self.echart5_data = {
'title': '词频分析',
'data':pm()
}
self.echart6_data = {
'title': '评论数据',
'data': biao()
}
self.map_1_data = {
# 'symbolSize': 80000,
'data':sheng()
}
爬虫主要代码:
html = requests.post(posturl, data=json.dumps(request), headers=headers)
html1 = json.loads(html.text)
print('正在爬取第'+str(i)+'页')
items = html1['result']['items']
for k in items:
try:
pl=k['content']
didian=k['ipLocatedName']
zongping=k['publishTypeTag']
pf1=k['scores'][0]['name']
pf1_score=k['scores'][0]['score']
pf2 = k['scores'][1]['name']
pf2_score = k['scores'][1]['score']
pf3 = k['scores'][2]['name']
pf3_score = k['scores'][2]['score']
taocan=k['touristTypeDisplay']
vip=k['userInfo']['userMember']
print(pl,didian,zongping,pf1,pf1_score,pf2,pf2_score,pf3,pf3_score,taocan,vip)
ws.append([pl,didian,zongping,pf1_score,pf2_score,pf3_score,taocan,vip])
except:
pass
可视化主要代码:
tooltip: {
show: true,
formatter: function(params) {
if (params.value.length > 1) {
return ' ' + params.name + ' ' + params.value[2] + '热度 ';
} else {
return ' ' + params.name + ' ' + params.value + '热度 ';
}
},
},
geo: {
map: 'china',
show: true,
roam: false,
label: {
emphasis: {
show: false
}
},
layoutSize: "100%",
itemStyle: {
normal: {
borderColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#00F6FF'
}, {
offset: 1,
color: '#53D9FF'
}], false),
borderWidth: 3,
shadowColor: 'rgba(10,76,139,1)',
shadowOffsetY: 0,
shadowBlur: 60
}
}
},
series: [{
type: 'map',
map: 'china',
aspectScale: 0.75,
//zoom:1.1,
label: {
normal: {
show: false,
},
emphasis: {
show: false,
}
},
itemStyle: {
normal: {
areaColor: {
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: '#073684' // 0% 处的颜色
}, {
offset: 1,
color: '#061E3D' // 100% 处的颜色
}],
},
borderColor: '#215495',
borderWidth: 1,
},
emphasis: {
areaColor: {
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: '#073684' // 0% 处的颜色
}, {
offset: 1,
color: '#061E3D' // 100% 处的颜色
}],
},
}
},
data: outdata,
}, {
type: 'effectScatter',
coordinateSystem: 'geo',
rippleEffect: {
brushType: 'stroke'
},
showEffectOn: 'render',
itemStyle: {
normal: {
color: {
type: 'radial',
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [{
offset: 0,
color: 'rgba(5,80,151,0.2)'
}, {
offset: 0.8,
color: 'rgba(5,80,151,0.8)'
}, {
offset: 1,
color: 'rgba(0,108,255,0.7)'
}],
global: false // 缺省为 false
},
}
},
label: {
normal: {
show: true,
color: '#fff',
fontWeight: 'bold',
position: 'inside',
formatter: function(para) {
return '{cnNum|' + para.data.value[2] + '}'
},
rich: {
cnNum: {
fontSize: 13,
color: '#D4EEFF',
}
}
},
},
symbol: 'circle',
symbolSize: function(val) {
if (val[2] === 0) {
return 0;
}
var a = (maxSize4Pin - minSize4Pin) / (max - min);
var b = maxSize4Pin - a * max;
return a * val[2] + b * 1.2;
},
data: convertData(outdata),
zlevel: 1,
}]
};