
每年三四月份毕业设计群里就开始被同一类题目刷屏Python旅游数据流量预测可视化平台。这个题目乍一看很综合Python语言、Django框架、机器学习算法、可视化展示全占了还挂着一个旅游出行场景感觉什么都能写但很多人一动手就卡住——数据不知道从哪来模型不知道怎么接进Web图表渲染出来也不好看。这篇内容我会把从零搭建到最终答辩演示的完整流程拆开讲包括技术选型的真实理由、数据怎么处理、线性回归怎么落地、大屏怎么做出来以及我实际操作中踩过的坑和应对方式希望能给正在做这个方向的同学一份可以直接照着做的参考。1. 项目定位与技术选型先想清楚毕设要什么1.1 别急着写代码先把技术边界圈出来拿到这个题目的第一反应很多人是装上Python、创建Django项目然后直接开写结果写到一半发现数据没有、模型不知道咋接、图表画不出来最后只能退回“景区信息管理系统”这种老路上。这里我建议一个做法任何毕设题目先别动键盘拿一页纸把技术边界画出来。这个项目的技术边界非常典型我按层列一下后端框架Django负责页面渲染、API接口、数据库管理数据处理pandas 负责清洗、特征工程SQLite/MySQL 负责存储机器学习scikit-learn 的 LinearRegression 做客流量预测joblib 保存模型可视化ECharts 通过 Ajax 拉取 Django 接口在前端渲染趋势图、排行图、分布图。把这条链路画清楚之后你再看整个项目就会非常通透数据进来经过清洗和特征工程一部分送去训练模型一部分存入数据库Web 端通过接口把历史数据和模型预测结果取出来渲染成图表。答辩时你只需要把这杯链路讲明白老师就知道你是真的做过而不是东拼西凑。1.2 为什么是 Django而不是 Flask 或 FastAPI我经常被问到“现在FastAPI这么火为什么不用它做毕设”我的回答很简单毕设追求的从来不是“接口性能极致的API服务”而是“完整工程链路”。Flask和FastAPI本身没有问题但你选了它们前端页面渲染、后台管理、表单处理这些环节都要你自己搭工期会明显拉长。Django 自带 Admin 后台、ORM、模板系统和表单功能一个框架就把“数据录入端、管理端、展示端”全包圆了。比如景区信息录入、客流数据维护注册到 admin 之后就能直接用后台可视化操作这一块在答辩演示时本身就是一个加分项。Django 的 ORM 还能让你在开发阶段用 SQLite后期想切到 MySQL 只需要改配置业务代码基本不用动。还有一个很现实的原因网上搜Django的资料比搜FastAPI多太多了Django的MTV模式也更容易理解——Model管数据表Template管页面View管业务逻辑。你遇到报错把错误信息直接丢到搜索框里基本都能找到对应的解决方案。对在校生来说试错成本低比什么都重要。1.3 项目结构怎么组织才不混乱这个项目我建议按下面的结构组织代码travel_dashboard/ ├── manage.py ├── travel/ │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── analyze/ │ ├── models.py │ ├── views.py │ ├── admin.py │ ├── urls.py │ └── ml/ │ ├── train_model.py │ └── linear_model.pkl └── templates/ └── dashboard.html主项目叫 travel_dashboard业务应用叫 analyze模型训练相关代码统一放在 analyze/ml 目录下。这样分层的好处是算法代码和 Web 逻辑彻底分离你调试模型的时候不需要动 Django 视图改页面的时候也不会影响模型接口。这里有一个我反复强调的坑不要在 views.py 里做模型训练。训练是一次性、耗时而且依赖数据文件的操作正确做法是单独跑训练脚本把产物保存成 pkl 文件Web 服务启动后只负责加载模型。训练和推理分离页面加载速度才不会受影响这也是实际工程项目里的常见习惯。2. 数据准备与特征工程预测准不准七成看这里2.1 旅游数据从哪里来三条路按需选“我拿不到真实数据”是这个题目下最常见的哭诉但其实有三条路可以走按省事程度排序第一条路使用公开数据集。政府统计部门发布的节假日旅游统计、景区年度游客量统计很多都能在公开渠道找到 Excel 或 CSV 文件。虽然粒度不一定细到“每天每个景区”但做毕设足够用了。第二条路自己生成模拟数据。别觉得模拟数据丢人只要你按照真实分布规律生成并在论文里写清楚“本项目数据为模拟数据用于验证系统流程”完全没有问题。生成方法其实不难用 numpy 生成随机数再叠加一个正弦波模拟季节性波动比如夏季高、冬季低工作日低、周末高再加上节假日脉冲和随机噪声出来的数据曲线就已经很像真实客流了。第三条路写爬虫采集公开旅游网站的实时数据。这条路能用但我必须提醒一句控制请求频率、遵守网站的 robots 协议和开放接口文档不要采集付费数据合规性一定要把住别给自己和指导老师惹麻烦。无论走哪条路最终都要落到一张统一结构的数据表里字段至少包含日期、景区ID、客流量、温度、降水量、星期几、是否节假日、是否周末。这些字段是后续模型特征的原材料少了任何一个后面特征工程都会被动。2.2 数据清洗和特征处理三个必做动作拿到的原始数据基本都不干净至少要做三件事。第一缺失值处理。如果某天的客流量为空先看缺失比例。占比低于 5%直接删除对应行高于 5%用前后两天的均值填充。这里不要图省事用整张表的均值旅游数据的季节差异非常大用全局均值填充会把训练集里的分布搞失真。第二异常值处理。一条突然飙到十万人次的记录可能是录入错误也可能是真有大型活动。最简单的筛查方式是三倍标准差法均值上下 3 个标准差之外的点先标记出来人工判断是保留还是剔除。宁可少一个点不要让异常值把模型系数拉偏。第三特征工程。把日期拆成年、月、日、星期把节假日映射成 0/1把周末也映射成 0/1。这一步非常关键因为线性回归的输入必须是数值。你不能把一个“2025-04-01”直接塞给模型它理解不了文本日期必须拆成模型能用的数字特征。特征准备好之后还有一个特别容易踩的坑训练集和测试集不能随机切分。时间序列数据一旦随机切分模型相当于提前看到了“未来”的信息测试集评估结果会虚高答辩时老师一问就露馅。正确做法是按日期排序后前 80% 作为训练集后 20% 作为测试集模拟“用过去预测未来”的真实场景。2.3 线性回归为什么够用怎么落地线性回归的核心假设是目标值可以由若干特征线性表示y β0 β1·x1 β2·x2 ... βn·xn模型要做的就是通过最小二乘法找到一组系数 β让所有样本的残差平方和最小。数学推导不复杂sklearn 也把整个逻辑封装好了你真正需要理解的是“系数正负代表什么”——正系数表示该特征和客流量同向变化负系数表示反向影响。比如周末标记的系数是正数就说明周末客流量整体高于工作日这个结论在论文里可以展开写一小段业务解释很有说服力。我带过的学生里有人一上来就想上 LSTM、Transformer我通常劝他们先做线性回归。原因有三个第一毕设能拿到的数据量也就是几百到几千条深度学习很容易过拟合测试集效果甚至不如线性回归第二答辩时间有限老师问“线性回归原理是什么”你三句话能讲清楚问“注意力机制是什么”就不一定能讲清楚了第三毕设评分看重的是完整闭环不是谁的模型参数量大。先用线性回归把整条链路跑通后面有余力再做模型对比实验这是最稳的路线。评估指标方面主要看两个R² 和 RMSE。R² 越接近 1 表示预测效果越好低于 0 说明模型比直接取均值还差RMSE 的单位和客流量一致能直观反映平均误差在什么量级。答辩时把这两个数值记在脑子里老师问起来你能脱口而出印象分会好很多。2.4 数据泄漏时间序列建模里最隐蔽的坑这个话题值得单独拿出来说。很多人拿着 train_test_split 直接随机切一刀R² 高到 0.95自己还觉得模型很牛。实际上这是典型的数据泄漏随机切分时同一个月、同一个周的样本很可能同时出现在训练集和测试集里模型已经提前见过了相似样本评估指标自然虚高。我在 train_model.py 里的处理方式是先按日期排序再按索引切分df df.sort_values(date).reset_index(dropTrue) split_idx int(len(df) * 0.8) train_df df.iloc[:split_idx] test_df df.iloc[split_idx:]切分完之后我强烈建议你画一张“真实值 vs 预测值”的曲线对比图。如果两条线在测试集区间贴合得不错说明模型真的学到了周期性规律而不是死记硬背。这张图放进论文里视觉冲击力很强答辩展示比单纯贴一个 R² 数字有说服力得多。3. 可视化大屏设计让评委一眼看懂你的成果3.1 可视化选型ECharts是首选没有之一可视化方案不少我直接给你一张对比表技术方案难度视觉效果适用场景说明ECharts低很强Web页面图表、大屏免费开源资料多图表类型丰富PyECharts低很强快速生成HTMLPython封装ECharts适合离线生成报表Chart.js低中等轻量简单图表功能相对基础复杂交互吃力AntV G2中强数据分析场景学习曲线略陡峭可视化大屏模板平台低炫演示型大屏模板化配置灵活性有限接模型能力较弱我推荐直接用 ECharts原因有三个一是图表类型多折线图、柱状图、饼图、地图、雷达图全都有大屏需要的元素基本全覆盖二是数据驱动方式非常适合 Django 的 JSON 接口后端吐什么前端画什么联调效率很高三是社区资源极其丰富随便搜一个“ECharts 客流趋势”就能找到现成的配置项改改数据字段就能用。3.2 大屏布局信息层级怎么排才好看毕设演示的时候视觉大屏几乎是拉分利器。布局我习惯这样安排顶部平台标题加时间筛选器占满整行高度约 8%中部主体从左到右分成三列宽度比例约 3:4:3左列放“热门景区 TOP10”柱状图和“游客来源地分布”饼图中间放一张区域地图散点图展示各景区热度视觉上最醒目右列放“客流量历史趋势”折线图和“未来7天预测”柱状对比图。页面上的图表数量控制在 4 到 6 个少了显得单薄多了显得杂乱。整体配色建议用深色底加大面积高亮色这是大屏展示的通用思路深色背景能明显提升图表的视觉聚焦感。用 CSS Grid 或 Flex 布局就能实现不需要引额外的 UI 框架。3.3 Django 后端怎么给前端“喂”数据前端需要的数据我建议用统一格式的 JSON 接口提供。以趋势图接口为例返回一个列表里面的元素是“日期 - 客流量”的字典前端拿到之后直接 map 出 x 轴和 y 轴数组剩下的交给 ECharts 处理。更关键的是预测接口。模型训练好之后在 views.py 里加载 pkl 文件把未来 7 天的特征拼好调一次 model.predict() 就能拿到一组预测值。这个接口是整个大屏最核心的亮点——评委一眼就能看出页面不只是在展示历史数据而是能真正做预测。加载模型代码有一点要特别提醒不要在每个请求里反复读 pkl 文件。正确做法是用模块级变量加懒加载import joblib _model None def get_model(): global _model if _model is None: _model joblib.load(analyze/ml/linear_model.pkl) return _model后续请求直接复用同一个模型对象这个细节在答辩时提一句老师会觉得你确实有工程意识而不只是会调包。4. 完整实操流程从零到可演示的毕设全记录4.1 环境准备与依赖安装这个项目对 Python 版本要求不苛刻3.9 以上都行但我强烈建议你先建虚拟环境不要全局装包。步骤非常简单python -m venv venv venv\Scripts\activate # Windows source venv/bin/activate # macOS / Linux pip install django pandas scikit-learn joblib openpyxlopenpyxl 是 pandas 读取 Excel 文件的依赖缺了它会报 ImportError。国内网络安装慢的话在 pip 命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple速度会明显提升。我见过太多人卡在装依赖这一步建议直接把镜像源写进 pip 配置别硬等默认源。4.2 创建 Django 项目和应用项目和应用的名字不要用中文也不要用 test 这种容易和关键字冲突的名字。命令如下django-admin startproject travel_dashboard cd travel_dashboard python manage.py startapp analyze创建完应用后记得在 settings.py 的 INSTALLED_APPS 里加上 analyze。这个动作忘掉的话后面 makemigrations 会报“无法识别模型”虽然能排查出来但没必要浪费时间。模板目录我习惯配置到项目根目录下的 templates 文件夹这样大屏页面放在全局目录里更好维护。4.3 定义数据模型景区与客流记录在 analyze/models.py 里定义两张表ScenicSpot 保存景区基本信息TrafficRecord 保存每天的客流量和相关特征。完整代码如下from django.db import models class ScenicSpot(models.Model): name models.CharField(景区名称, max_length100) city models.CharField(所在城市, max_length50) level models.CharField(景区等级, max_length10, blankTrue) category models.CharField(景区类型, max_length50, blankTrue) def __str__(self): return self.name class TrafficRecord(models.Model): spot models.ForeignKey(ScenicSpot, on_deletemodels.CASCADE, verbose_name关联景区) date models.DateField(日期) visitors models.IntegerField(客流量) temperature models.FloatField(温度, default0) precip models.FloatField(降水量, default0) holiday models.BooleanField(是否节假日, defaultFalse) def __str__(self): return f{self.spot.name} {self.date} class Meta: ordering [date]定义完之后执行迁移python manage.py makemigrations analyze python manage.py migrate别忘了在 admin.py 里注册模型这样后台可以直接管理景区和客流数据。演示的时候顺便打开 admin 页面给老师看计算机专业毕设里“后台管理”仍然是传统加分项。4.4 训练线性回归模型并保存在 analyze/ml 目录下新建 train_model.py逻辑是读取数据、特征工程、按时间切分、训练线性回归、保存模型、打印评估指标。import pandas as pd import joblib from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error df pd.read_excel(data/tourism_raw.xlsx, parse_dates[date]) df df.sort_values(date).reset_index(dropTrue) df[year] df[date].dt.year df[month] df[date].dt.month df[day] df[date].dt.day df[weekday] df[date].dt.weekday df[is_weekend] df[weekday].isin([5, 6]).astype(int) features [month, day, weekday, is_weekend, holiday, temperature, precip] X df[features] y df[visitors] split_idx int(len(X) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2:, r2_score(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) joblib.dump(model, analyze/ml/linear_model.pkl)这里我有意没有用 train_test_split就是为了避免时间序列数据的随机切分问题。如果你选用的数据里 holiday 字段还是“是/否”这种文本训练前需要先做一次映射df[holiday] df[holiday].map({是: 1, 否: 0})。训练完你会得到一个 pkl 文件这个文件是 Django 视图和模型之间的桥梁。4.5 视图、路由与预测接口views.py 里我放了三个动作dashboard 渲染大屏页面api_trend 返回历史客流数据api_predict 返回未来 7 天预测值。核心代码如下import joblib from datetime import date, timedelta from django.http import JsonResponse from django.shortcuts import render from .models import ScenicSpot, TrafficRecord _model None def get_model(): global _model if _model is None: _model joblib.load(analyze/ml/linear_model.pkl) return _model def dashboard(request): spots ScenicSpot.objects.all() return render(request, dashboard.html, {spots: spots}) def api_trend(request): spot_id request.GET.get(spot_id, 1) qs TrafficRecord.objects.filter(spot_idspot_id).order_by(date) data [{date: r.date.strftime(%Y-%m-%d), visitors: r.visitors} for r in qs] return JsonResponse(data, safeFalse) def api_predict(request): m get_model() results [] today date.today() for i in range(1, 8): d today timedelta(daysi) row [[d.month, d.day, d.weekday(), 1 if d.weekday() in (5, 6) else 0, 0, 25, 0]] pred m.predict(row)[0] results.append({date: d.strftime(%Y-%m-%d), pred_visitors: round(pred, 2)}) return JsonResponse(results, safeFalse)预测接口里我把未来节假日记为 0温度固定用 25是因为简化处理。如果你想做得更完整可以在前端加一个表单让用户输入未来天气和节假日安排预测结果会更灵活。目前这个接口已经足够展示“模型不是摆设真的在算未来几天客流”这个核心点了。路由配置同样简单from django.urls import path from . import views urlpatterns [ path(, views.dashboard, namedashboard), path(api/trend/, views.api_trend, nameapi_trend), path(api/predict/, views.api_predict, nameapi_predict), ]把 analyze 的 urls 挂载到主项目的 urls.py 之后启动开发服务器python manage.py runserver访问http://127.0.0.1:8000就能看到页面。4.6 ECharts 大屏页面的核心渲染代码模板页面的核心是几个 div 容器加对应的图表初始化脚本。趋势图这段代码最具代表性div idtrendChart styleheight: 400px;/div script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script script fetch(/api/trend/?spot_id1) .then(res res.json()) .then(data { var chart echarts.init(document.getElementById(trendChart)); chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(d d.date) }, yAxis: { type: value, name: 客流量 }, series: [{ name: 客流量, type: line, smooth: true, areaStyle: {}, data: data.map(d d.visitors) }] }); }); /script预测对比图也可以用类似方式把 api_predict 返回的数组画成柱状图。跑通之后给页面加个全屏按钮和定时刷新答辩演示效果会很好。ECharts 的 dataZoom 组件建议加上它能让评委拖动查看不同时间段的数据交互感会明显提升。5. 高频问题排查与答辩应对5.1 运行期容易踩的坑速查表问题现象可能原因处理方法makemigrations 提示 No changes应用没有注册到 INSTALLED_APPS在 settings.py 中加入 analyze页面图表空白接口 404 或返回结构不对F12 打开 Network先直接访问接口确认 JSON中文乱码模板没有声明 utf-8HTML 头部加meta charsetutf-8预测值全部相同特征没有区分度或数据泄漏检查特征分布改用时间顺序切分pkl 文件加载路径报错相对路径受工作目录影响用settings.BASE_DIR / analyze/ml/linear_model.pklECharts 图表加载失败CDN 地址失效或网络受限替换可用 CDN或把 echarts.min.js 下载到本地 static 目录路径问题是最容易被忽略的Django 开发服务器的当前工作目录和命令行执行脚本时可能不一样相对路径容易飘。用项目根目录拼接绝对路径最稳妥换机器也不怕。5.2 模型效果不理想时的改进路线如果 R² 在 0.5 以下先别急着换模型按这个顺序排查。第一加特征比如“距最近节假日的天数”“景区是否有大型活动”这类辅助信息。第二做特征缩放温度、降水量这些连续特征和 is_holiday 这种离散特征量纲差异大先用 StandardScaler 标准化。第三给月份、温度加多项式特征处理非线性关系但注意不要加太多避免过拟合。第四做模型对比实验随机森林、梯度提升树都可以放进论文的对比部分哪怕最终系统还是用线性回归对比实验本身就是加分项。答辩时如果被问到“为什么数据量小的时候深度学习反而不如线性回归”可以这样回答深度学习依赖大量数据拟合复杂映射关系样本量少、模型容量大时容易把训练集的噪声也背下来泛化能力反而差线性回归参数少、归纳偏置强在小样本场景下更稳定。这句话建议记下来在模型选型的追问环节几乎是万金油答案。5.3 被问到“大数据、大模型”时怎么接住题目里挂了“大数据、大模型”答辩时大概率会被追问“你这套系统离大数据平台还有多远”我的建议是分两层回答。存储与计算层面当前项目用 SQLite/MySQL 存的是单机规模数据如果数据量到达 TB 级会把存储层换成分库分表或引入离线批处理框架处理历史数据再加消息队列做实时流计算形成离线与实时两条链路。模型层面当前没有强行接入大模型是因为线性回归在这个任务里已经能满足需求后续可以扩展一个智能问答助手用大模型解析用户的自然语言查询自动生成统计结果和解释。这个回答既承认当前做法的合理性又展示了扩展方向评委一般会满意。有一点要提醒不要在答辩时吹“我已经用了大数据平台、大模型”一旦老师追问细节你就很难圆场。把“当前够用、接口可扩展、未来可演进”讲清楚比虚张声势效果好得多。5.4 做这类项目的一点实际建议我个人带毕业设计的感受是这类题目的最大优势在于它的拼图属性——你不需要在算法、工程、可视化任何一个单点做到顶尖只需要把数据、模型、接口、图表完整串起来就是一个非常合格的作品。很多学生容易陷入死磕算法或者死磕前端两个极端其实对毕设而言能端到端跑通、能讲清楚每一步的输入输出、能应对两三个追问就已经是优秀水平的作品了。动手做的时候给自己留一个“最小可演示版本”。哪怕先只用一个景区、一张趋势图、一个预测接口先让整条链路转起来再慢慢加功能和美化页面。这个思路不只适用于这个题目几乎所有毕设项目都能套用。按这个节奏推进过程中遇到问题就回来翻一翻这篇里的排查表你会发现这条路比想象中顺畅得多。