背景
随着旅游行业的快速发展,数据在旅游决策和规划中的重要性日益凸显。基于 Python Flask 的旅游数据大屏实现研究旨在结合爬虫技术和数据库存储,为用户提供全面、实时的旅游信息展示平台。
爬虫技术作为数据采集的重要手段,能够从各种网络数据源中高效地抓取和提取信息。通过自动化爬虫程序,可以实现对各大旅游网站、景点信息平台和交通服务网站的数据采集,实时获取旅游相关数据并保持更新。
数据库的运用则能够有效管理和存储大量的旅游数据,为后续的数据处理和展示提供支持。通过建立数据库模型和利用 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实现主要代码:
def echart6(self):
# 去除停用词
def drop_stopwords( contents, stopword_list):
contents_clean = []
all_words = []
for line in contents:
line_clean = []
for word in line:
# 过滤停用词
if word in stopword_list:
continue
# 过滤词个数小于等于1的词语
if len(word) <= 1:
continue
# 过滤包含数字和特殊符号、英文的词语
if re.search(r'\d|\W|[a-zA-Z]', word):
continue
line_clean.append(word)
all_words.append(str(word))
contents_clean.append(line_clean)
return contents_clean, all_words
engine = create_engine(
'mysql+pymysql://root:root@localhost:3306/旅游数据可视化'
)
sql='select 简介 from 旅游数据详情;'
one = pd.read_sql(sql, con=engine)
stopwords = pd.read_csv("停用词.txt", index_col=False, sep="\t", quoting=3, names=['stopword'], encoding='utf-8')
print(stopwords.head())
content = one[one['简介'].notnull()]['简介'].values.tolist()
content_S = []
爬虫主要代码:
for j in url:
try:
res=requests.get(j[0],headers).text
soup = BeautifulSoup(res, 'lxml')
dw = soup.find_all('li')
for i in dw:
try:
chengshi=j[1]
jingdian = i.find('div', class_="title").find('b').text
print(jingdian)
xingji=j[2]
print(xingji)
xiaoliang = int((i.find('div', class_="title").find('span').text.split('人'))[0])
print(xiaoliang)
danjia = i.find_all('span', class_="price")
dj = int(danjia[0].find('b').text) if danjia else 1
print(dj)
lianjie=i.find('a').get('href')
print(lianjie)
a.append(chengshi)
b.append(jingdian)
c.append(xingji)
d.append(xiaoliang)
e.append(dj)
g.append(lianjie)
可视化主要代码:
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,
}]
};