☰
智能旅游推荐系统毕设源码解析:Python+Vue+MySQL从还原到部署
2026/10/3 2:46:28 网站建设 项目流程

简介:这是基于Python与MySQL的智能旅游推荐系统毕业设计完整项目包,适合计算机相关专业毕业生、Python Web开发学习者以及需要快速搭建旅游类管理系统的开发者。系统覆盖首页、个人中心、用户管理、旅游资讯、景点信息、酒店信息、行程分享、交流论坛等核心模块,并配有系统分析与设计、数据库设计、详细设计说明,帮助读者理解从需求梳理到编码实现的全过程。资源包共799个文件,压缩包大小25.71MB,主要由Python源码(45个py、40个pyc)、Vue前端页面(53个vue、164个js)、数据库SQL脚本(2个sql)以及设计文档(docx、md)构成,另附运行脚本、配置文件和静态素材,便于本地搭建与二次开发。已有231人学习下载。读者可从中获得可直接运行的旅游推荐网站原型、建表SQL、前后端模块划分与设计文档,既支持毕业设计参考与答辩演示,也能基于现有模块扩展智能推荐策略、优化后台管理或补充移动端。

1. 智能旅游推荐系统源码包:打开之前先搞清这三件事

临到交毕设才发现手里的代码东缺一块西缺一块,这种滋味我太熟了。这份智能旅游推荐系统源码包,是一个基于 Python + MySQL 的完整毕设项目:后端用 Python 提供接口和数据服务,前端由 Vue 组件拼出管理后台,覆盖首页、个人中心、用户管理、旅游资讯、景点信息、酒店信息、行程分享、交流论坛、系统管理这一整套功能,还带着安装脚本、运行脚本和说明文档。对打算做 Python Web 方向毕设、又不想从零搭框架的同学来说,它可以直接当底座。下面我从目录结构拆到数据库设计和推荐逻辑,再讲运行时真正会踩的坑,按顺序走完基本能跑起来。

2. 源码目录与 Python + Vue 技术栈:.bak 后缀和三段式结构怎么处理

2.1 解开 zip 包之后,第一步不是看代码,是把 .bak 还原

资源拿过来是一个 zip 压缩包。Windows 下我习惯右键“全部解压缩”,这一步很简单,但解压完你会看到一堆以 .bak 结尾的文件:index.html.bak、update-password.vue.bak、IndexMain.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak。初次接触这种命名的人十有八九会懵:这些文件到底是源码还是没用的备份?

.bak 在这里的意思是备份副本,本质就是源码文件本身。开发同学一边改程序一边留副本是很常见的习惯,打包的时候没清理扩展名,就一起塞进 zip 了。所以你要做的第一件事,就是把这些 .bak 后缀去掉,让 index.html.bak 回到 index.html,让 update-password.vue.bak 回到 update-password.vue。文件名里真正的后缀是 .html 和 .vue,别把整个名字换成别的。

还原之后,这套系统的轮廓就清楚了。它是一个典型的前后端分离结构:index.html 是单页入口,后台的壳由 IndexMain.vue、IndexAsideStatic.vue、IndexHeader.vue 和 BreadCrumbs.vue 四件套拼出来;update-password.vue 是后台的修改密码页;Python 后端负责接收前端请求、读写 MySQL、返回 JSON 数据。这种“Vue 做界面、Python 做数据服务”的组合,是近几年毕设里非常主流的写法。

文件角色注意事项
index.html.bak单页入口去掉 .bak,确认挂载点 id
IndexMain.vue.bak主内容区菜单切换后核心内容在这里渲染
IndexAsideStatic.vue.bak侧边栏菜单用户管理、景点管理等入口在这里定义
IndexHeader.vue.bak顶部导航栏用户信息和退出按钮
BreadCrumbs.vue.bak面包屑导航展示当前页面层级
update-password.vue.bak修改密码页前端校验逻辑比较完整,适合入门读

2.2 Python 后端承担什么:接口、数据库和业务流程

这套系统里的 Python 不是写个脚本跑一遍就完事,而是提供一个常驻的 Web 服务。毕设的常见做法是用 Flask 这类轻量框架搭应用,路由层接收前端请求,模型层通过 MySQL 驱动读写数据,业务层处理登录、景点信息增删改查、酒店管理、行程分享提交、论坛回复这些操作。

我一般会先看“安装.bat”里帮你装了什么依赖。这个批处理文件的典型作用是调用 pip install 把项目依赖全部装好。如果压缩包里没有 requirements.txt,那你至少需要装 pymysql(连接 MySQL 用)和 Flask(Web 框架),这两个是起步最基本的,别的一边跑一边补。

写接口时路径通常会按模块划分,比如 /api/user/login、/api/news/list、/api/scenic/list。前端页面通过这些路径向后端要数据,后端再拼接 SQL 去查表。第 3 章我会拿推荐接口出来拆,那是最能体现“智能”两个字的地方。

2.3 动手之前先配置 Python 环境:VS Code 解释器选对再装包

说 IDE 的事。用 VS Code 打开这个项目时,推荐先把 Python 解释器指到项目环境里。按 Ctrl+Shift+P 打开命令面板,输 Python: Select Interpreter,选你准备装依赖的那个 Python。这一步不做,后面会出现“pip 明明装了 pymysql,运行却报 ModuleNotFoundError”的怪事,原因就是解释器选错了。

然后批量还原 .bak 文件。Windows 下的 PowerShell 可以这样写:

Get-ChildItem -Path . -Filter *.bak | Rename-Item -NewName { $_.Name -replace '\.bak$', '' }

这段命令的逻辑是:把当前目录下所有以 .bak 结尾的文件找出来,逐个去掉后缀。两个细节要注意:-Filter *.bak 只匹配文件,不会动到文件夹;-replace 里的 .bak$ 是正则写法,那个反斜杠是把点号转义成普通字符,$ 表示匹配到字符串结尾,这样只会去掉末尾的 .bak,不会误伤文件名中间的 bak 字样。如果你在 Mac 或 Linux 上操作,等价写法是 for f in *.bak; do mv "$f" "${f%.bak}"; done。

还原之后对照目录检查一遍:前端页面文件和后端 Python 文件应该在同一个项目根下,说明文档是一个独立的压缩包,里面有摘要、系统分析与设计、数据库设计和详细设计几个部分。如果页面文件嵌在子目录里,注意看后端代码里引用模板或静态文件的相对路径,别让路径对不上。

3. 数据库与推荐算法实现:从建表 SQL 到推荐接口返回 JSON

3.1 核心表结构:用户、景点、酒店、行程和论坛

智能推荐依赖的数据来自一张张普通的关系表。按照说明文档里数据库设计章节的描述,MySQL 里至少要建 7 张表才能撑起摘要里列的功能。我把功能清单和表对应起来理一理:用户表管前台用户和后台管理员;旅游资讯表存资讯文章;景点信息表和景点分类表是推荐的主体数据;酒店信息表给住宿推荐提供数据;行程分享表存用户发布的行程;交流论坛相关的帖子表存标题、内容和回复关联。

建表时字符集必须统一,否则后面中文全变问号。我一般先建库再建表,SQL 写成这样:

CREATE DATABASE IF NOT EXISTS travel_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE travel_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50) DEFAULT '', avatar VARCHAR(255) DEFAULT '', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_scenic ( id INT PRIMARY KEY AUTO_INCREMENT, scenic_name VARCHAR(100) NOT NULL, category_id INT NOT NULL, address VARCHAR(255) DEFAULT '', price DECIMAL(10,2) DEFAULT 0, click_count INT DEFAULT 0, img_url VARCHAR(255) DEFAULT '', content TEXT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这两条 SQL 执行后,travel_db 库里就有了用户表和景点表。字段类型上,id 用自增主键,username 加 UNIQUE 防重复注册,价格用 DECIMAL(10,2) 而不是 FLOAT,免得算钱丢精度,正文类字段用 TEXT。如果你是从说明文档里抄建表语句,重点检查三处:表名和后端 SQL 引用是否一致、字段名是不是统一的下划线风格、外键字段类型和主表主键是否一致。这三类问题在答辩前最容易爆雷,而且错得悄无声息。

表用途关键字段
t_user用户与管理员username, password, nickname
t_scenic景点信息scenic_name, category_id, click_count
t_hotel酒店信息hotel_name, price, address
t_travel_note行程分享user_id, title, content
t_forum_post论坛帖子user_id, title, reply_count
t_news旅游资讯title, content, publish_time

3.2 推荐算法:先按分类过滤,再按热度排序

很多同学看到“智能旅游推荐”六个字,以为里面藏了什么深度神经网络。其实毕设阶段的推荐基本是两板斧:先按照用户选择的分类或历史行为过滤数据,再按点击量、时间、评分这些可量化指标排序。这套系统的实现思路也在这个范围内,推荐接口做的事大致是:用户访问首页时,后端读一个分类参数,从景点表里筛出对应分类的数据,按点击量倒序取前 N 条返回;用户没指定分类,就把全表按点击量和新旧程度加权排序。

我用 Python 写核心排序逻辑,常见做法是在路由函数里拼 SQL 再取结果集:

def recommend_scenic(category_id=None, limit=6): conn = get_connection() cursor = conn.cursor() if category_id: sql = """ SELECT id, scenic_name, price, click_count, category_id FROM t_scenic WHERE category_id = %s ORDER BY click_count DESC LIMIT %s """ cursor.execute(sql, (category_id, limit)) else: sql = """ SELECT id, scenic_name, price, click_count, category_id FROM t_scenic ORDER BY (click_count * 0.7 + (UNIX_TIMESTAMP(create_time) / 100000) * 0.3) DESC LIMIT %s """ cursor.execute(sql, (limit,)) rows = cursor.fetchall() cursor.close() conn.close() return [dict(zip([col[0] for col in cursor.description], row)) for row in rows]

这段代码的逻辑分两路。带了 category_id 时走精确分类,ORDER BY click_count 就是按热度排序,LIMIT %s 控制返回条数;没带分类时走综合排序,点击量占 0.7 权重,创建时间的 Unix 时间戳除以 10 万再乘 0.3,时间越新权重越高。两个细节说明一下:cursor.description 用来取列名,先把每行转成字典,方便前端直接用字段名取值;所有参数都用 %s 占位传值,不要用字符串拼接,否则 SQL 注入直接送走答辩。如果想引入价格或评分因素,把字段加进 ORDER BY 表达式调权重就行。

3.3 接口封装与返回格式:前端要的是一份约定好的 JSON

后端写完推荐查询逻辑,还要把它暴露成一个 HTTP 接口。Flask 里写一个路由,接收参数,调用查询函数,返回 JSON:

from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/api/scenic/recommend', methods=['GET']) def scenic_recommend(): category_id = request.args.get('category_id', type=int) limit = request.args.get('limit', default=6, type=int) data = recommend_scenic(category_id=category_id, limit=limit) return jsonify({ "code": 0, "msg": "success", "data": data }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)

这个接口接收两个 GET 参数:category_id 是景点分类 ID,可选;limit 是返回条数,默认 6。返回结构统一成 code、msg、data 三层,code 为 0 表示成功,data 里放景点列表。前端拿到后直接遍历渲染,错误分支根据 code 判断。request.args 从 URL 查询串取参数,type=int 做类型强转,传了非数字时 Flask 会直接返回 400 错误,这在联调阶段比后端报 500 好定位得多。host 用 0.0.0.0 而不是 127.0.0.1,是为了让同一局域网的手机也能访问这个接口,演示时不用一直对着电脑。

4. 前端 Vue 组件与联调:管理后台页面怎么改成自己的项目

4.1 index.html 入口与后台布局是怎么拼出来的

前端这一层,源码包给的是 Vue 风格的单页应用结构。index.html.bak 是入口页,里面一般会有一个挂载点

,然后通过 script 标签引入 Vue 和组件文件,由组件控制整个后台界面的渲染。很多毕设为了省事不会用 webpack 打包,而是直接引入 vue.min.js 再声明组件,这对你反而是好事——不用学 Node 打包工具也能改。

IndexMain.vue 管主内容区,就是后台中间那块会随菜单切换变化的区域。IndexAsideStatic.vue 是左侧静态菜单栏,系统里的“用户管理、旅游资讯管理、景点信息管理、景点分类管理、酒店信息管理、行程分享管理、交流论坛、系统管理”这些入口,基本都在这个组件里定义。IndexHeader.vue 是顶部导航栏,放标题、用户信息和退出按钮。BreadCrumbs.vue 是面包屑导航,告诉你当前在哪个页面层级。把这四个组件的模板读一遍,后台的骨架就清楚了。

4.2 改密页面 update-password.vue:一个完整的表单交互样本

源码包里 update-password.vue.bak 是修改密码页,它在所有后台页面里最建议先读:功能独立、逻辑短,适合做前后端交互的切入口。这个页面的核心交互是三步:用户输入旧密码、新密码、确认新密码,前端校验两次新密码一致后调用后端接口,后端校验旧密码正确后更新数据库。

我把毕设里常见的提交逻辑抽出来,核心部分长这样:

// update-password.vue 中的提交逻辑 methods: { submitForm() { if (this.form.newPassword !== this.form.confirmPassword) { alert('两次输入的新密码不一致'); return; } axios.post('/api/user/updatePassword', { userId: this.userId, oldPassword: this.form.oldPassword, newPassword: this.form.newPassword }).then(res => { if (res.data.code === 0) { this.$message.success('密码修改成功'); } else { this.$message.error(res.data.msg); } }); } }

这段代码做了两层校验:前端先拦截两次密码不一致的情况,避免无效请求打到后端;后端返回的 code 再决定页面提示成功还是失败。实际项目里务必在后端加一层 userId 与登录 session 的匹配校验,防止越权改别人密码。参数方面,newPassword 传到后端后要加密再入库,明文存储是答辩评委最爱挑的毛病。

4.3 前后端联调:把请求地址统一收口到一个变量里

页面里如果每个请求都把 IP 和端口写死在 axios 调用处,换一台电脑演示就要全局替换。我习惯在前端建一个 base.js,或者直接在 index.html 里定义一个全局常量,把接口地址收口:

// 全局接口地址配置 const BASE_URL = 'http://127.0.0.1:5000'; // 统一封装 get 请求 function request(url, params) { return axios.get(BASE_URL + url, { params }) .then(res => res.data) .catch(err => { console.error('接口请求失败', url, err); }); }

后面所有页面都用 request('/api/scenic/recommend', { category_id: 2 }) 这种形式调用。换后端端口时只改 BASE_URL 一处,全局生效。这里踩过一个坑:浏览器直接访问 Flask 接口没问题,但 Vue 页面从另一个端口发起请求时会触发跨域,需要在 Flask 侧配 CORS。常见做法是安装 flask-cors 然后在应用上调用一次 CORS(app),否则请求会被浏览器拦下来,控制台报的错误和接口本身没关系,容易误判到后端代码上。

5. 运行与部署排查:依赖安装、中文乱码、端口冲突五个常见坑

先说结论:这份源码包跑不起来的案例里,绝大多数栽在 .bak 没还原和数据库连不上这两步。下面按排查顺序写,每一条都是现象、原因、解决三步走。

5.1 pip 装包超时中断,装到一半报错退出

现象:执行安装.bat 或者手动 pip install flask pymysql 时,下载进度条走一会儿就卡住,最后报 timeout 或者 connection error。

原因:默认的 PyPI 源在国外,国内网络环境下访问速度慢、时常断连,尤其是教学网里装几十 MB 的包,超时是常态。

解决:换成清华或者阿里镜像源。命令写法是 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 。如果项目里没有 requirements.txt,就手工一条条装,pip install flask pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple 。装完用 pip list 看一眼版本,Flask 用 2.x 和 3.x 的差异在 Python 版本上,如果代码里用了 Flask-Migrate 这类扩展,注意版本要匹配,不然 import 阶段就报错。

5.2 写入数据库的中文变成问号

现象:通过页面新增一个景点,名称里带中文,存进 MySQL 后查出来是一串 ??。

原因:建库时字符集没指定 utf8mb4,MySQL 沿用了默认的旧字符集,中文存不进去。

解决:把库和表的字符集一次改到位:

ALTER DATABASE travel_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ALTER TABLE t_scenic CONVERT TO CHARACTER SET utf8mb4;

第一条改库默认字符集,第二条把已有表的字段字符集转换掉。注意:已经存成乱码的数据,转换后不会自动恢复,把脏数据删掉重新导入。所以建表之前就要确认字符集,这是最省事的路径。

5.3 运行.bat 启动后终端直接闪退,报 address already in use

现象:双击运行.bat,终端窗口刚弹出来就报错关闭,细看提示里有一句端口占用。

原因:5000 端口被其他程序占了。最常见的是上一次后端进程没关干净,或者系统里别的服务恰好用了 5000。

解决:Windows 下执行 netstat -ano | findstr :5000,找到处于 LISTENING 状态的 PID,然后 taskkill /PID 那个数字 /F。如果这个端口被系统服务占用不好强杀,就把后端代码里 app.run 的 port 改成 5001,同时把前端 BASE_URL 里的端口一并改掉。改端口一定要改两处,只改后端不改前端,页面照样连不上。

5.4 打开 index.html 白屏,控制台报组件找不到

现象:浏览器打开 index.html,页面一片空白,F12 控制台报 Failed to load resource,提示找不到 IndexMain.vue 或者其它组件文件。

原因:.bak 文件没有还原,浏览器加载入口页时找不到真正要执行的组件文件。这个坑最容易卡人,因为报错信息直指文件缺失,很多人会误以为压缩包本身是坏的。

解决:回到第 2.3 节,在项目根目录执行那段 PowerShell 批量重命名命令,把 .bak 全部还原。还原完刷新浏览器,组件就能正常加载了。判断依据很简单:看项目目录里有没有干净的 index.html 和 update-password.vue,有说明还原成功。

5.5 明明装了 pymysql,运行还是报 ModuleNotFoundError

现象:pip list 显示 pymysql 已经在列表里,可程序一运行还是提示找不到模块。

原因:VS Code 当前选中的 Python 解释器,不是装 pymysql 的那个环境。这种情况在电脑里装了多个 Python 版本时特别常见,终端里用的 Python 和编辑器里用的不是同一个。

解决:Ctrl+Shift+P 打开命令面板,输 Python: Select Interpreter,选 pip 装包时对应的那个 Python。如果项目目录下有 venv 文件夹,就选 .\venv\Scripts\python.exe 这个路径。选完重启终端再跑一次,症状大概率消失。这块在 python 入门阶段最容易浑,属于环境配置里典型的黑匣子,花时间搞清楚一次,后面省很多事。

6. 把推荐逻辑改成自己的参数:权重调整、数据替换和答辩前自测三件事

拿到这份源码最忌讳的就是只改标题就交。我的习惯是先从推荐逻辑动手,把第 3 章那个排序权重调成自己的参数。可以把权重单独抽成一个配置块:

# recommend_config.py RANK_WEIGHT = { "click": 0.5, # 热度权重 "time": 0.2, # 时间权重 "price_score": 0.3 # 价格友好分 }

排序前先算每个景点的综合分,再以综合分倒序取前 N 条。这样答辩时问你“为什么推荐这个景点”,你能清楚说出点击量、时间、价格三个因素的加权结果,比一句“按点击量排的”听起来扎实得多。数据替换也简单,把 t_scenic 表里的景点数据清空,导入你自己城市或目标地区的真实景点,图片链接放网上素材地址,前端展示就完全是另一个项目的样子。

第二件事是自测。启动后我一般按顺序走一遍完整链路:用测试账号登录、进后台改密码、新增一个景点、把它顶到首页推荐位、去前台看展示、发一条行程分享、在交流论坛回一个帖子。任何一步报错都记下来,改完再跑一遍。这套流程走完,功能层面基本不会在演示时突然翻车。

我还习惯把数据库导出成一份 SQL 备份放在项目外的目录,万一现场误操作把数据清了,还能快速还原。从那以后,我每次接手这种源码包,都会强制先走“还原 .bak、配解释器、改库字符集、启动自测”这四步,再开始动业务代码;顺序不变,翻车概率小很多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询