💕💕作者:计算机源码社
💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流!
💕💕学习资料、程序开发、技术解答、文档报告
💕💕如需要源码,可以扫取文章下方二维码联系咨询
💕💕Java项目
💕💕微信小程序项目
💕💕Android项目
💕💕Python项目
💕💕PHP项目
💕💕ASP.NET项目
💕💕Node.js项目
💕💕选题推荐
项目实战|基于Python的广东热门旅游数据可视化分析系统
文章目录
1、选题背景
随着旅游业的快速发展和互联网技术的普及,越来越多的游客依赖于在线旅游平台获取旅游信息和规划行程。广东作为中国的旅游大省,拥有丰富的旅游资源和众多热门景点。然而,海量的旅游数据分布在各大网站上,缺乏系统性和直观的展示方式,难以为游客提供高效、全面的信息支持。因此,开发一个基于Python的广东热门旅游数据可视化分析系统,通过爬虫技术获取并分析旅游数据,能够为用户提供直观的视觉化分析和数据展示,帮助用户更好地了解和选择旅游目的地。
2、研究目的和意义
本系统的开发旨在通过爬虫技术自动获取广东热门旅游景点的相关数据,并通过数据清洗、处理,将这些数据进行系统化存储与管理。进一步通过数据可视化技术,将复杂的旅游信息以更加直观的方式呈现出来,帮助用户快速了解旅游景点的各类指标,如景点热度、评分、评论数量等。系统还提供用户注册、登录、个人信息管理以及景点信息管理等功能,旨在为用户和管理者提供一个全面、高效的旅游数据管理与分析平台。
基于Python的广东热门旅游数据可视化分析系统不仅能够提升旅游信息的获取与处理效率,还能通过可视化手段,增强数据的可读性和易用性,助力决策过程。对于游客而言,系统所提供的各类数据分析和展示,能够为其旅游决策提供科学的参考,节省时间和精力。对于旅游管理者和研究者而言,系统则可以作为数据分析的有力工具,通过分析热门景点的热度、评价等信息,帮助优化旅游资源配置,提高旅游服务质量,同时也为未来的旅游市场发展策略提供数据支持。
3、系统研究内容
本课题旨在设计与实现一个基于Python的广东热门旅游数据可视化分析系统,通过多种技术手段对广东热门旅游景点数据进行采集、处理、存储及可视化展示。具体研究内容包括以下几个方面:
本课题采用Scrapy爬虫技术,从携程网站中自动化地抓取广东省热门旅游景点的相关数据。这些数据包括景点名称、景点热度、评分、评论数量等重要指标。通过构建高效的爬虫脚本,确保数据采集的全面性与准确性。
本课题对获取的数据进行处理与清洗,包括去除重复数据、处理缺失值及规范化数据格式等,以提高数据的质量和一致性。此过程确保了后续数据存储与分析的可靠性。清洗后的数据被存储到MySQL数据库中,便于后续的数据管理与调用。
接下来本课题使用Echarts可视化框架进行数据的展示与分析。系统通过大屏可视化分析,直观地展示广东热门旅游景点的相关信息。具体展示内容包括景点名称的词云图、景点热度可视化、评分可视化、评论数量可视化、景点总数统计等。这些可视化结果不仅有助于用户了解各景点的受欢迎程度和用户反馈,还能为旅游相关的市场分析和决策提供数据支持。
最后本课题开发了一个基于Python和Flask框架的Web网站,提供用户注册、登录、个人信息管理、景点信息管理和系统公告管理等功能。用户可以通过该网站查看上述可视化结果,管理自己的账户信息,并获得系统公告和通知。该系统为用户提供了一个集成化的旅游数据分析与管理平台,实现了数据采集、处理、存储、分析与展示的全过程。
4、系统页面设计
如需要源码,可以扫取文章下方二维码联系咨询
5、参考文献
[1]刘燕楠.基于爬虫技术对云冈石窟景区评论的情感分析与可视化展示[J].电脑知识与技术,2024,20(21):74-76.DOI:10.14004/j.cnki.ckt.2024.1082.
[2]张名扬,王子俊,罗兴稳,等.基于协同过滤算法的旅游景点可视化分析系统的设计与实现[J].长江信息通信,2024,37(07):21-23.DOI:10.20153/j.issn.2096-9759.2024.07.007.
[3]南方.江西省数字文化服务平台底层支撑系统的建设与应用[J].广播电视网络,2024,31(06):92-96.DOI:10.16045/j.cnki.catvtec.2024.06.021.
[4]陈文婷,沈丽珍,汪侠.旅游信息流对假期客流的时空影响效应研究------以淄博市为例[J].地理科学进展,2024,43(06):1156-1166.
[5]简易,林岩,张书涵.基于POI数据的徐州市旅游景点空间分布特征及影响因素研究[J].智能建筑与智慧城市,2024,(06):36-39.DOI:10.13655/j.cnki.ibci.2024.06.010.
[6]王馨梓,李鹏,马楠.GIS在旅游业的应用现状与建模方法[J].绿色科技,2024,26(11):205-211.DOI:10.16663/j.cnki.lskj.2024.11.024.
[7]陈成伟,陆福运.基于虚幻引擎的交旅融合服务区可视化系统技术与实现[J].大众科技,2024,26(03):21-24.
[8]孟文杰,孙晓瑜,王政凯,等.数智化背景下山东文旅数据中台设计[J].无线互联科技,2024,21(10):93-95.
[9]徐春,王萌萌,孙彬.融合知识图谱和图注意力网络的旅游推荐算法[J].计算机工程与设计,2024,45(05):1420-1427.DOI:10.16208/j.issn1000-7024.2024.05.019.
[10]彭珺.基于事理图谱的网络游记文本知识融合研究[D].吉林大学,2024. DOI:10.27162/d.cnki.gjlin.2024.000344.
[11]李卫帆,刘晓静,毛磊,等.数据赋能特色旅游终端卷烟营销能力分析[J].财经界,2024,(13):57-59.DOI:10.19887/j.cnki.cn11-4098/f.2024.13.053.
[12]李斌,陈琳敏,张晓芳,等.旅游资源普查GIS空间数据库设计与实现[J].延安职业技术学院学报,2024,38(02):99-104.DOI:10.13775/j.cnki.cn61-1472/g4.2024.02.016.
[13]黄玉亭.数字化转型背景下旅游服务企业财务预决算模型的创新[J].金融客,2024,(04):36-38.
[14]孙雨露,路菁,姚铮.数字文旅视域下移动智慧旅游平台拓展技术集成研究[J].无线互联科技,2024,21(07):46-49.
[15]贾衍菊,李思雨.基于公众参与地理信息系统的乡村旅游区主客依恋空间表征差异研究[J].干旱区资源与环境,2024,38(06):191-199.DOI:10.13448/j.cnki.jalre.2024.133.
[16]伍进,王颖,洪万杰.国内旅游业扩展现实技术应用研究综述[J].乐山师范学院学报,2024,39(03):59-70.DOI:10.16069/j.cnki.51-1610/g4.2024.03.009.
[17]黄正鹏,马欣,陈雪,等.基于线性回归算法对多彩贵州旅游数据的分析及应用[J].软件工程,2024,27(03):63-66.DOI:10.19644/j.cnki.issn2096-1472.2024.003.013.
[18]范帅超,李华.基于GIS的历史文化街区应急疏散能力可视化评估研究[J].中国安全生产科学技术,2024,20(02):28-34.
[19]曹许悦,孙永华,王衍昭,等.湿地数字孪生技术及其应用综述[J].测绘通报,2024,(02):32-38.DOI:10.13474/j.cnki.11-2246.2024.0206.
[20]丁宇阳,胡涵,王莹露,等.基于区块链技术的旅游服务及可视化分析系统设计[J].长江信息通信,2024,37(02):166-169.DOI:10.20153/j.issn.2096-9759.2024.02.050.
6、核心代码
java
# # -*- coding: utf-8 -*-
# 数据爬取文件
import scrapy
import pymysql
import pymssql
from ..items import GuangdongItem
import time
from datetime import datetime,timedelta
import datetime as formattime
import re
import random
import platform
import json
import os
import urllib
from urllib.parse import urlparse
import requests
import emoji
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
from selenium.webdriver import ChromeOptions, ActionChains
from scrapy.http import TextResponse
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
# 广东景点
class GuangdongSpider(scrapy.Spider):
name = 'guangdongSpider'
spiderUrl = 'https://you.ctrip.com/sight/guangzhou152/s0-p{}.html;https://you.ctrip.com/sight/shenzhen26/s0-p{}.html;https://you.ctrip.com/sight/zhuhai27/s0-p{}.html;https://you.ctrip.com/sight/foshan207/s0-p{}.html;https://you.ctrip.com/sight/dongguan212/s0-p{}.html;https://you.ctrip.com/sight/shantou215/s0-p{}.html'
start_urls = spiderUrl.split(";")
protocol = ''
hostname = ''
realtime = False
headers = {
"Cookie":"输入自己的cookie"
}
def __init__(self,realtime=False,*args, **kwargs):
super().__init__(*args, **kwargs)
self.realtime = realtime=='true'
def start_requests(self):
plat = platform.system().lower()
if not self.realtime and (plat == 'linux' or plat == 'windows'):
connect = self.db_connect()
cursor = connect.cursor()
if self.table_exists(cursor, '1040ett3_guangdong') == 1:
cursor.close()
connect.close()
self.temp_data()
return
pageNum = 1 + 1
for url in self.start_urls:
if '{}' in url:
for page in range(1, pageNum):
next_link = url.format(page)
yield scrapy.Request(
url=next_link,
headers=self.headers,
callback=self.parse
)
else:
yield scrapy.Request(
url=url,
headers=self.headers,
callback=self.parse
)
# 列表解析
def parse(self, response):
_url = urlparse(self.spiderUrl)
self.protocol = _url.scheme
self.hostname = _url.netloc
plat = platform.system().lower()
if not self.realtime and (plat == 'linux' or plat == 'windows'):
connect = self.db_connect()
cursor = connect.cursor()
if self.table_exists(cursor, '1040ett3_guangdong') == 1:
cursor.close()
connect.close()
self.temp_data()
return
list = response.css('div.list_wide_mod2 div.list_mod2')
for item in list:
fields = GuangdongItem()
if '(.*?)' in '''div.rdetailbox dl dt a::text''':
try:
fields["title"] = str( re.findall(r'''div.rdetailbox dl dt a::text''', item.extract(), re.DOTALL)[0].strip())
except:
pass
else:
try:
fields["title"] = str( self.remove_html(item.css('''div.rdetailbox dl dt a::text''').extract_first()))
except:
pass
if '(.*?)' in '''dd.ellipsis::text''':
try:
fields["address"] = str( re.findall(r'''dd.ellipsis::text''', item.extract(), re.DOTALL)[0].strip())
except:
pass
else:
try:
fields["address"] = str( self.remove_html(item.css('''dd.ellipsis::text''').extract_first()))
except:
pass
if '(.*?)' in '''div.leftimg a img::attr(src)''':
try:
fields["pic"] = str( re.findall(r'''div.leftimg a img::attr(src)''', item.extract(), re.DOTALL)[0].strip())
except:
pass
else:
try:
fields["pic"] = str( self.remove_html(item.css('''div.leftimg a img::attr(src)''').extract_first()))
except:
pass
if '(.*?)' in '''b.hot_score_number::text''':
try:
fields["hotnumber"] = float( re.findall(r'''b.hot_score_number::text''', item.extract(), re.DOTALL)[0].strip())
except:
pass
else:
try:
fields["hotnumber"] = float( self.remove_html(item.css('b.hot_score_number::text').extract_first()))
except:
pass
if '(.*?)' in '''a.score strong::text''':
try:
fields["score"] = float( re.findall(r'''a.score strong::text''', item.extract(), re.DOTALL)[0].strip())
except:
pass
else:
try:
fields["score"] = float( self.remove_html(item.css('a.score strong::text').extract_first()))
except:
pass
if '(.*?)' in '''a.recomment::text''':
try:
fields["commentcount"] = int( re.findall(r'''a.recomment::text''', item.extract(), re.DOTALL)[0].strip().replace("条点评)","").replace("(",""))
except:
pass
else:
try:
fields["commentcount"] = int( self.remove_html(item.css('a.recomment::text').extract_first()).replace("条点评)","").replace("(",""))
except:
pass
if '(.*?)' in '''div.rdetailbox dl dt a::attr(href)''':
try:
fields["laiyuan"] = str( re.findall(r'''div.rdetailbox dl dt a::attr(href)''', item.extract(), re.DOTALL)[0].strip())
except:
pass
else:
try:
fields["laiyuan"] = str( self.remove_html(item.css('''div.rdetailbox dl dt a::attr(href)''').extract_first()))
except:
pass
detailUrlRule = item.css('div.rdetailbox dl dt a::attr(href)').extract_first()
if self.protocol in detailUrlRule or detailUrlRule.startswith('http'):
pass
elif detailUrlRule.startswith('//'):
detailUrlRule = self.protocol + ':' + detailUrlRule
elif detailUrlRule.startswith('/'):
detailUrlRule = self.protocol + '://' + self.hostname + detailUrlRule
else:
detailUrlRule = self.protocol + '://' + self.hostname + '/' + detailUrlRule
yield scrapy.Request(url=detailUrlRule, meta={'fields': fields}, headers=self.headers, callback=self.detail_parse, dont_filter=True)
# 详情解析
def detail_parse(self, response):
fields = response.meta['fields']
try:
fields["opentime"] = str( response.xpath('''.//div[p[contains(text(),"开放时间")]]//p[@class="baseInfoText cursor openTimeText"]//text()''').extract()[0].strip().replace("${column.delimiter}","").replace(" ","").replace(";",""))
except:
pass
try:
fields["phone"] = str( response.xpath('''.//div[p[contains(text(),"官方电话")]]//p[@class="baseInfoText"]//text()''').extract()[0].strip())
except:
pass
try:
fields["info"] = str( response.xpath('''.//div[contains(text(),"介绍")]/following-sibling::*[contains(@class, "moduleContent")][1]//text()''').extract()[0].strip().replace(" ",""))
except:
pass
return fields
# 数据清洗
def pandas_filter(self):
engine = create_engine('mysql+pymysql://root:123456@localhost/spider1040ett3?charset=UTF8MB4')
df = pd.read_sql('select * from guangdong limit 50', con = engine)
# 重复数据过滤
df.duplicated()
df.drop_duplicates()
#空数据过滤
df.isnull()
df.dropna()
# 填充空数据
df.fillna(value = '暂无')
# 异常值过滤
# 滤出 大于800 和 小于 100 的
a = np.random.randint(0, 1000, size = 200)
cond = (a<=800) & (a>=100)
a[cond]
# 过滤正态分布的异常值
b = np.random.randn(100000)
# 3σ过滤异常值,σ即是标准差
cond = np.abs(b) > 3 * 1
b[cond]
# 正态分布数据
df2 = pd.DataFrame(data = np.random.randn(10000,3))
# 3σ过滤异常值,σ即是标准差
cond = (df2 > 3*df2.std()).any(axis = 1)
# 不满⾜条件的⾏索引
index = df2[cond].index
# 根据⾏索引,进⾏数据删除
df2.drop(labels=index,axis = 0)
# 去除多余html标签
def remove_html(self, html):
if html == None:
return ''
pattern = re.compile(r'<[^>]+>', re.S)
return pattern.sub('', html).strip()
# 数据库连接
def db_connect(self):
type = self.settings.get('TYPE', 'mysql')
host = self.settings.get('HOST', 'localhost')
port = int(self.settings.get('PORT', 3306))
user = self.settings.get('USER', 'root')
password = self.settings.get('PASSWORD', '123456')
try:
database = self.databaseName
except:
database = self.settings.get('DATABASE', '')
if type == 'mysql':
connect = pymysql.connect(host=host, port=port, db=database, user=user, passwd=password, charset='utf8')
else:
connect = pymssql.connect(host=host, user=user, password=password, database=database)
return connect
# 断表是否存在
def table_exists(self, cursor, table_name):
cursor.execute("show tables;")
tables = [cursor.fetchall()]
table_list = re.findall('(\'.*?\')',str(tables))
table_list = [re.sub("'",'',each) for each in table_list]
if table_name in table_list:
return 1
else:
return 0
# 数据缓存源
def temp_data(self):
connect = self.db_connect()
cursor = connect.cursor()
sql = '''
insert into `guangdong`(
id
,title
,address
,pic
,hotnumber
,score
,commentcount
,opentime
,phone
,info
,laiyuan
)
select
id
,title
,address
,pic
,hotnumber
,score
,commentcount
,opentime
,phone
,info
,laiyuan
from `1040ett3_guangdong`
where(not exists (select
id
,title
,address
,pic
,hotnumber
,score
,commentcount
,opentime
,phone
,info
,laiyuan
from `guangdong` where
`guangdong`.id=`1040ett3_guangdong`.id
))
order by rand()
limit 50;
'''
cursor.execute(sql)
connect.commit()
connect.close()
💕💕作者:计算机源码社
💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流!
💕💕学习资料、程序开发、技术解答、文档报告
💕💕如需要源码,可以扫取文章下方二维码联系咨询