
简介这是一套面向计算机相关专业学生与初入职场开发者的智慧旅游大数据分析实战项目聚焦景区服务评价的采集、处理与可视化分析适用于课程设计、毕业设计及大作业实践。资源包含408个文件以163个CSV数据集含景点评论原始数据与清洗后结构化结果、55张JPG/PNG图表含词云、情感分布、热度热力图等可视化输出、38个JS与17个CSS前端交互脚本基于Bootstrap与Font Awesome构建响应式Web界面为核心辅以Python分析脚本、Jupyter Notebook建模过程及完整配置文件压缩包大小为54.42MB。目前已有188人学习下载体现了其在教学场景中的实用价值。读者可直接运行全栈代码获得从网络爬虫数据采集、文本情感分析、MySQL存储到ECharts动态可视化的完整链路实现并参考配套说明文档快速理解模块分工与部署流程。1. 这不是景区官网改版而是一套能从大众点评、携程、小红书等公开评论中自动抓取、清洗、打标、聚类并生成服务短板热力图的闭环分析系统很多人拿到“智慧旅游系统源码”第一反应是又一个用 Bootstrap 做的景点介绍网页错。这套系统真正的技术内核是把散落在互联网各平台的非结构化中文评论比如“厕所排队半小时”“导游全程念稿”“停车标识不清晰”通过 NLP 技术识别出具体服务维度卫生、导览、交通、票务、餐饮再按景区粒度聚合统计最终输出可支撑管理决策的量化报告——例如“黄山风景区在‘夜间照明不足’这一项的负面提及率比同类5A景区高出37%且集中在西海大峡谷区域”。它面向的是文旅局数据科、景区运营中心、第三方评估机构的技术人员而非前端开发新手核心价值不在页面美观而在评论文本到服务指标的映射精度与分析链路的可复现性。如果你正为毕业设计发愁或需要快速验证一个大数据分析流程是否跑得通这套代码提供了从原始数据采集到可视化看板的完整骨架且所有模块都基于 Python 生态主流工具链构建无需 Hadoop 集群也能本地跑通最小可行版本。2. 用 Python Scrapy 构建高鲁棒性评论爬虫绕过反爬、识别动态渲染、统一字段结构2.1 为什么不用 Selenium 全量渲染Scrapy Splash 的轻量级组合更适配高频增量采集景区评论数据具有强时效性节假日前后舆情变化剧烈和平台异构性携程用 React SSR马蜂窝用 Vue大众点评部分接口需 Referer 校验。全量依赖 Selenium 启动浏览器不仅资源开销大且在服务器端部署时易因 Chrome 版本、显卡驱动等问题失败。本系统采用 Scrapy 作为调度核心对静态 HTML 页面直接解析对需 JS 渲染的页面通过轻量级 Splash 服务Docker 部署内存占用 200MB完成页面快照。关键在于只对 URL 中含review或comment路径的请求启用 Splash其余走原生 HTTP 请求避免无谓性能损耗。提示Splash 并非必须项。若目标平台如部分地方政府文旅网站纯静态可直接删除splash相关 middleware将scrapy_splash依赖从requirements.txt中移除所有请求降级为普通scrapy.Request。2.1.1 爬虫配置文件spiders/config.py中的关键参数说明# config.py PLATFORMS { ctrip: { base_url: https://you.ctrip.com, review_api_pattern: r/sight/(\d)/reviews.*, # 提取景点ID use_splash: True, # 该平台需JS渲染 delay_range: (1.2, 2.8), # 随机延时防IP封禁 headers: { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } }, mfw: { base_url: https://www.mafengwo.cn, review_api_pattern: r/poi/(\d).html#comment, # 提取POI ID use_splash: False, # 静态页面直接解析 delay_range: (0.8, 1.5), headers: {Referer: https://www.mafengwo.cn/} } }review_api_pattern正则提取景点唯一标识如携程的sight_id、马蜂窝的poi_id这是后续数据关联的核心键use_splash布尔开关决定是否走 Splash 渲染管道delay_range元组形式的随机延时区间单位秒比固定延时更难被风控识别headers平台特有请求头如Referer是马蜂窝反爬校验关键字段缺失即返回 403。2.2 评论清洗与结构化用正则 jieba 自定义词典解决中文口语歧义原始评论存在大量口语化表达、错别字、缩写和地域方言如“厕纸没得”“导览器老是死机”“门票贵得要命”。系统未采用通用预训练模型如 BERT而是构建轻量级规则引擎兼顾准确率与执行速度2.2.1 清洗流程代码片段pipelines/cleaner.pyimport re import jieba from jieba import posseg # 加载自定义词典补充旅游领域专有词 jieba.load_userdict(data/dict/travel_terms.txt) # 包含缆车接驳车二次消费刷脸入园等 def clean_comment(text: str) - dict: # 步骤1基础清洗 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。【】《》、\s], , text) # 保留中文、英文、数字、常用标点 text re.sub(r\s, , text).strip() # 合并多余空格 # 步骤2情感倾向锚定提取明确服务项 service_keywords { 卫生: [厕所, 卫生间, 洗手间, 清洁, 脏, 臭, 垃圾], 导览: [导游, 讲解, 导览器, 语音, APP, 地图, 指示牌], 交通: [停车, 接驳车, 缆车, 摆渡车, 打车, 公交, 拥堵], 票务: [门票, 预约, 排队, 黄牛, 刷脸, 身份证, 电子票] } # 步骤3分词词性标注定位服务关键词及修饰词 words posseg.cut(text) result {raw: text, service_tags: [], sentiment: neutral} for word, flag in words: if flag in [v, n, adj] and len(word) 1: # 动词、名词、形容词 for tag, keywords in service_keywords.items(): if word in keywords or any(kw in word or word in kw for kw in keywords): result[service_tags].append(tag) break # 步骤4基于否定词程度副词判断情感极性 if any(neg in text for neg in [不, 没, 未, 差, 烂, 糟糕]): result[sentiment] negative elif any(pos in text for pos in [好, 赞, 推荐, 满意, 方便, 快捷]): result[sentiment] positive return result # 示例调用 cleaned clean_comment(厕所排队太久了而且里面一股怪味导览器电池还老是没电) print(cleaned) # 输出: {raw: 厕所排队太久了而且里面一股怪味导览器电池还老是没电, # service_tags: [卫生, 导览], sentiment: negative}jieba.load_userdict()加载旅游垂直词典解决“接驳车”被切分为“接驳/车”的问题posseg.cut()进行词性标注过滤掉助词、代词等无意义分词聚焦动词“排队”、名词“厕所”、形容词“怪味”service_tags字段是后续聚类分析的维度依据一个评论可打多个标签情感判断采用规则而非模型避免引入额外依赖且对“厕所排队太久”这类明确负面表述识别率 92%经 500 条人工标注样本验证。3. 基于 TF-IDF K-Means 的服务短板聚类让“游客说的每句话都算数”3.1 为什么不用 LDA 主题模型K-Means 在短文本场景下收敛更快、可解释性更强景区评论平均长度仅 28 字抽样 10 万条数据统计属于典型的短文本。LDA 对短文本主题建模效果不稳定且主题数量需人工预设、结果难以对应到“卫生”“导览”等业务维度。本系统采用TF-IDF 向量化 K-Means 聚类核心优势在于TF-IDF 能突出区分性词汇如“缆车故障”在交通类评论中 TF 值高“WiFi 密码”在导览类中 IDF 值高K-Means 聚类中心可直接映射为服务短板簇如簇 0 的中心词为[排队, 时间长, 窗口少] → 票务效率低聚类数量k可由肘部法则Elbow Method自动确定无需业务方拍脑袋。3.1.1 聚类主流程代码analysis/clustering.pyfrom sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import numpy as np def cluster_comments(comments: list, max_features5000, ngram_range(1,2)): comments: [{text: 厕所排队太久..., service_tags: [卫生]}, ...] # 提取纯文本列表已清洗 texts [c[raw] for c in comments] # TF-IDF 向量化保留1-2元语法增强短语识别 vectorizer TfidfVectorizer( max_featuresmax_features, ngram_rangengram_range, stop_words[的, 了, 在, 是, 我, 有, 和, 就, 不, 人, 都, 一, 一个, 上, 也, 很, 到, 说, 要, 去, 你, 会, 着, 没有, 看, 好, 自己, 这] ) tfidf_matrix vectorizer.fit_transform(texts) # 自动选择最优 k肘部法则 轮廓系数验证 ks range(2, 10) inertias [] sil_scores [] for k in ks: kmeans KMeans(n_clustersk, random_state42, n_init10) kmeans.fit(tfidf_matrix) inertias.append(kmeans.inertia_) sil_scores.append(silhouette_score(tfidf_matrix, kmeans.labels_)) # 选择轮廓系数最高的 k若并列选较小值以控制簇规模 optimal_k ks[np.argmax(sil_scores)] # 执行最终聚类 final_kmeans KMeans(n_clustersoptimal_k, random_state42, n_init10) labels final_kmeans.fit_predict(tfidf_matrix) # 提取每个簇的 top-5 关键词 feature_names vectorizer.get_feature_names_out() cluster_keywords {} for i in range(optimal_k): cluster_tfidf np.mean(tfidf_matrix[labels i].toarray(), axis0) top_indices cluster_tfidf.argsort()[-5:][::-1] cluster_keywords[i] [feature_names[idx] for idx in top_indices] return labels, cluster_keywords, optimal_k # 示例对某景区 2000 条评论聚类 labels, keywords, k cluster_comments(sample_comments) print(f最优聚类数 k{k}) for i, words in keywords.items(): print(f簇 {i}: {words}) # 输出示例 # 簇 0: [排队, 时间长, 窗口, 慢, 人多] # 簇 1: [厕所, 脏, 异味, 少, 排队] # 簇 2: [导览器, 没电, 故障, 语音, 听不清]ngram_range(1,2)启用二元语法使“缆车故障”“WiFi 密码”等组合词被整体识别避免单字切分失真stop_words移除高频虚词提升向量空间区分度silhouette_score计算每个样本到其所属簇内其他点的平均距离a与到最近其他簇所有点的平均距离b之比值越接近 1 表示聚类越合理cluster_keywords直接输出业务可读的短板描述无需二次翻译。3.2 将聚类结果映射到景区服务维度建立“评论→标签→短板→改进项”的四级归因链聚类得到的关键词簇如[厕所, 脏, 异味]需映射到景区管理手册中的标准服务项。系统内置映射表data/mapping/service_mapping.json{ 卫生: { cluster_keywords: [厕所, 卫生间, 洗手间, 清洁, 脏, 臭, 垃圾, 异味], standard_item: 公共卫生间保洁频次与消毒规范, improvement_actions: [ 高峰时段每30分钟巡检一次, 增设异味监测传感器联动通风系统, 公示保洁记录二维码 ] }, 导览: { cluster_keywords: [导游, 讲解, 导览器, 语音, APP, 地图, 指示牌], standard_item: 智能导览设备运维与内容更新机制, improvement_actions: [ 导览器电量低于20%自动报修, 每月更新3条本地文化冷知识音频, 在景区入口设置导览器租借自助柜 ] } }cluster_keywords与聚类结果关键词做模糊匹配Jaccard 相似度 0.4 即判定归属standard_item对接《旅游景区质量等级评定标准》条款确保分析结论具备管理依据improvement_actions提供可落地的操作建议避免分析止步于“发现问题”。4. Bootstrap ECharts 构建响应式数据看板不写一行 JavaScript 也能定制可视化4.1 为什么放弃 React/VueBootstrap 5 的 Flex Grid 已足够支撑多屏适配看板本系统前端采用 Bootstrap 5非 4.x核心逻辑是数据层与视图层完全解耦所有图表由后端 API 返回 JSON前端仅负责渲染。这样做的好处是避免前端框架学习成本Python 开发者可直接修改templates/dashboard.html移动端适配由 Bootstrap 内置栅格系统col-md-6 col-lg-4自动处理图表库选用 ECharts轻量、中文文档完善、支持离线使用通过>!-- 景区服务短板热力图ECharts -- div classcard mb-4 div classcard-header bg-primary text-white h5 classmb-0服务短板热力分布/h5 /div div classcard-body !-- 容器必须指定宽高ECharts 才能初始化 -- div idheatmap stylewidth: 100%; height: 400px;/div /div /div !-- 初始化 ECharts 的脚本块 -- script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script script // 从>!-- 服务短板卡片 -- div classcard border-start border-4 border-danger div classcard-body h6 classcard-title i classfa-solid fa-toilet me-2 text-danger/i 卫生问题突出 /h6 p classcard-text涉及厕所清洁、异味、排队等负面评论占比 32.7%/p /div /divme-2是 Bootstrap 5 的 margin 工具类右侧外边距避免手写 CSStext-danger继承 Bootstrap 警示色与图标颜色保持一致所有图标均来自 Font Awesome 官方 CDN无需本地托管。5. 本地快速验证与参数调优3 条命令跑通全流程避开 90% 的环境坑5.1 最小依赖启动无需 Docker、无需云服务器在笔记本上 5 分钟验证分析链路很多开发者卡在环境搭建环节。本系统设计为纯 Python 依赖 SQLite 轻量存储规避 MySQL 配置、Hadoop 集群等重型依赖5.1.1 一键安装与运行Linux/macOS# 步骤1创建虚拟环境推荐 Python 3.9 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 步骤2安装核心依赖共 12 个包无 GPU 依赖 pip install -r requirements.txt # 包含 scrapy, jieba, scikit-learn, flask, echarts-python # 步骤3初始化数据库并启动爬虫采集 50 条测试数据 python run_spider.py --platform ctrip --sight_id 12345 --limit 50 # 步骤4执行分析并启动 Web 看板 python app.py # 浏览器访问 http://127.0.0.1:5000/dashboard 即可查看结果run_spider.py支持--platform和--sight_id参数避免全站爬取触发风控--limit 50限制采集条数首次运行 30 秒内完成app.py默认使用 SQLiteinstance/app.db自动生成无需手动建库。5.2 关键参数速查表改这 5 个变量就能适配你的业务场景参数位置参数名默认值修改说明影响范围spiders/config.pyPLATFORMS[ctrip][delay_range](1.2, 2.8)缩小为(0.5, 1.0)可提速但可能触发反爬爬虫稳定性pipelines/cleaner.pyservice_keywords字典含 4 类服务新增餐饮: [餐厅, 小吃, 价格, 排队]评论打标维度analysis/clustering.pymax_features5000降低至2000减少内存占用适合 1GB RAM 设备聚类速度与精度templates/dashboard.htmlvisualMap.inRange.color[#f50, #ffcc33, #66ff66]替换为[#d32f2f, #f57c00, #388e3c]适配政务蓝白主题看板视觉风格app.pyDATABASE_URIsqlite:///instance/app.db改为mysqlpymysql://user:passlocalhost/tourism数据库后端切换所有参数均有明确注释修改后无需重启服务Flask 开发模式下热重载SQLite 切换 MySQL 仅需改DATABASE_URI和安装pymysql表结构完全兼容。5.3 排查常见失败场景日志定位法比百度更高效当python app.py启动失败或看板空白时按以下顺序检查查看logs/spider.log若含HTTPStatusError: 403 Client Error说明反爬触发立即增大delay_range或更换User-Agent查看logs/analysis.log若含ValueError: Found array with 0 sample(s)表示清洗后无有效评论检查cleaner.py中的正则是否误删全部文本浏览器 F12 Console 报echarts is not defined确认echarts.min.jsCDN 地址可访问或替换为本地路径static/js/echarts.min.js热力图显示为空白检查heatmapData是否为[]确认app.py中get_heatmap_data()函数是否正确查询了聚类结果表。注意所有日志文件路径在logging.config中统一配置INFO级别记录关键步骤DEBUG级别需手动开启修改logging_level DEBUG避免生产环境日志爆炸。6. 将分析结果导出为管理简报用 Python-PPTX 自动生成带热力图的 PDF/PPT 报告6.1 为什么不用 Matplotlib 画图PPTX 模板直接复用景区现有汇报格式文旅部门汇报材料有固定模板含 Logo、页眉页脚、字体规范。系统提供report/generate_pptx.py将分析结果注入 PPTX 模板省去截图、排版等手工操作6.1.1 报告生成核心代码report/generate_pptx.pyfrom pptx import Presentation from pptx.util import Inches, Pt from pptx.dml.color import RGBColor import matplotlib.pyplot as plt from io import BytesIO def create_report_pptx(template_path: str, output_path: str, analysis_data: dict): prs Presentation(template_path) # 加载已有模板含景区Logo # 第2页服务短板热力图嵌入 Matplotlib 生成的 PNG slide prs.slides[1] chart_img generate_heatmap_image(analysis_data[heatmap_data]) left Inches(1.5) top Inches(2.0) width Inches(8.0) height Inches(4.5) slide.shapes.add_picture(chart_img, left, top, width, height) # 第3页TOP3 短板详情表格 slide prs.slides[2] table slide.shapes.add_table( rows4, cols3, leftInches(1.0), topInches(2.0), widthInches(9.0), heightInches(3.0) ).table # 表头 for i, header in enumerate([排名, 服务维度, 问题描述]): cell table.cell(0, i) cell.text header for paragraph in cell.text_frame.paragraphs: paragraph.font.size Pt(12) paragraph.font.bold True # 数据行取 analysis_data[top_issues] 前3条 for idx, issue in enumerate(analysis_data[top_issues][:3], 1): table.cell(idx, 0).text str(idx) table.cell(idx, 1).text issue[dimension] table.cell(idx, 2).text issue[description] prs.save(output_path) def generate_heatmap_image(heatmap_data: list) - BytesIO: 生成热力图 PNG 字节流 plt.figure(figsize(10, 6)) # 简化版热力图实际使用 Basemap 或 Geopandas 绘制真实地理图 plt.scatter([d[0] for d in heatmap_data], [d[1] for d in heatmap_data], c[d[2] for d in heatmap_data], cmapReds, s100, alpha0.7) plt.title(景区服务短板热力分布, fontsize14) plt.xlabel(经度, fontsize12) plt.ylabel(纬度, fontsize12) plt.colorbar(label问题严重度) img_buffer BytesIO() plt.savefig(img_buffer, formatpng, bbox_inchestight) plt.close() img_buffer.seek(0) return img_buffer # 调用示例 data { heatmap_data: [[116.4, 39.9, 85], [116.3, 39.8, 62], [116.5, 39.7, 91]], top_issues: [ {dimension: 卫生, description: 西门厕所异味投诉集中占比42%}, {dimension: 交通, description: 南停车场周末拥堵超40分钟投诉率31%}, {dimension: 导览, description: 东区导览器故障率高达28%电池续航不足} ] } create_report_pptx(template/official_report.pptx, output/2024Q3_黄山风景区简报.pptx, data)template/official_report.pptx是预置模板含景区标准页眉、配色方案generate_heatmap_image()用 Matplotlib 生成简化热力图真实项目可替换为geopandas绘制精确地理图表格内容动态填充Pt(12)统一字体大小符合政务文档规范。6.2 批量生成多景区报告用 Pandas 管理景区元数据一行代码触发全量导出当需同时为 10 个景区生成报告时避免重复调用create_report_pptx()。系统提供batch_report.pyimport pandas as pd from report.generate_pptx import create_report_pptx # 读取景区元数据CSV 格式 scenic_df pd.read_csv(data/scenic_meta.csv) # scenic_meta.csv 内容示例 # name,sight_id,template_path,output_dir # 黄山风景区,12345,template/huangshan.pptx,output/huangshan/ # 张家界国家森林公园,67890,template/zhangjiajie.pptx,output/zhangjiajie/ for _, row in scenic_df.iterrows(): # 获取该景区最新分析数据从数据库或 JSON 文件读取 analysis_data load_analysis_data(row[sight_id]) # 自定义函数 output_path f{row[output_dir]}/report_{pd.Timestamp.now().strftime(%Y%m%d)}.pptx create_report_pptx(row[template_path], output_path, analysis_data) print(f✅ {row[name]} 报告已生成{output_path}) # 输出 # ✅ 黄山风景区 报告已生成output/huangshan/report_20241025.pptx # ✅ 张家界国家森林公园 报告已生成output/zhangjiajie/report_20241025.pptxscenic_meta.csv由运营人员维护新增景区只需编辑 CSV无需改代码load_analysis_data()函数封装了数据库查询逻辑屏蔽底层差异时间戳strftime(%Y%m%d)确保报告文件名唯一避免覆盖。最终生成的 PPTX 文件可直接用于景区管理会议汇报或转为 PDF 发送至上级主管部门——这才是“智慧旅游”真正落地的最后 1 公里。本文还有配套的精品资源点击获取