新闻详情

新闻详情

首页 / 资讯中心 / 详情

旅游评论情感分析系统毕设实战:爬虫、NLP与前后端分离全流程

发布时间:2026/10/1 8:25:45来源:尧图网络
旅游评论情感分析系统毕设实战:爬虫、NLP与前后端分离全流程
简介基于Python语言开发的旅游景点评论情感分析毕业设计项目集成携程、马蜂窝爬虫采用前后端分离架构。面向计算机、通信、人工智能、自动化等专业的学生、老师或从业者既可用于期末课程设计、课程大作业也可作为毕业设计的完整参考方案解决景点评论数据采集、文本情感分析及结果展示等关键问题。zip压缩包共102个文件大小约47.71MB其中32个py源码承载爬虫、情感分析、后端接口等核心逻辑10个vue组件与6个js脚本搭建前端界面4个json、3个html及md/txt文档辅助配置与使用说明结构清晰便于按模块查看。项目为答辩评审98分的个人毕设全部代码经过调试测试可运行覆盖从数据采集、文本预处理、情感分类到可视化展示的完整链路既有新手友好的学习价值也有较强的扩展空间有基础者可根据需要调整爬虫目标、优化模型或增加图表。目前已有228人学习下载适合作为计算机、人工智能等相关方向的课题参考与二次开发基础。1. 这套旅游评论情感分析系统98 分毕设里真正能跑起来的东西先说结论这是一套前后端分离的旅游景点评论情感分析项目爬虫端覆盖携程和马蜂窝两个平台分析端对评论做情感极性判定最终通过注册登录、景点筛选、结果图表把整个流程串起来。作者在答辩里拿到 98 分不是靠概念堆出来的而是因为每个环节都有可演示的产物——你打开前端页面选一个景点系统能从爬虫抓取一直跑到情感分布图渲染。对于要做 Python 毕业设计、课程大作业或者想入门爬虫与 NLP 结合的从业者来说这套系统的价值在于它把爬虫、数据清洗、情感分析、前后端交互四条线完整打通了。我拆完这套资源后的直观感受是代码量不大但工程结构比大多数“只有一个 notebook”的毕设完整得多值得照着复现一遍。2. 前后端分离的工程骨架Quasar 前端与 Python 后端的启动顺序2.1 先从 quasar.conf.js 看懂前端的真实身份这套系统前端目录里有 index.html、result.html、index.template.html、quasar.conf.js稍微熟悉 Vue 生态的人一眼就能认出来这是 Quasar 框架的项目结构。Quasar 是一套基于 Vue 的 UI 框架官方定位是“一套代码编译成 SPA、SSR、PWA 甚至移动端”毕设用它来写后台管理类页面非常合适因为表格、表单、图表组件开箱即用不用像手写 Bootstrap 那样拼半天。quasar.conf.js 是这个前端项目的核心配置文件里面主要做三件事声明要用哪些 Quasar 插件比如 Loading、Notify、配置 dev server 的端口和代理、定义构建产物的输出路径。注意这里的 dev server 端口默认是 8080而后端 Python 服务我拆下来看默认跑在 5000 或 8000 端口两者不同源所以前端请求后端接口时大概率会遇到跨域问题——这一点我会在第 5 章专门讲实际上这个项目里已经内置了代理方案但你如果直接双击 index.html 打开是跑不起来的必须通过 quasar dev 启动。2.2 后端 Python 服务的路由设计与数据流向后端的核心逻辑按模块拆分爬虫模块负责抓取评论情感分析模块负责对评论文本打分API 层负责把结果暴露成 HTTP 接口。一个典型的数据流向是用户在前端选择景点 → 前端调用后端/api/analyze接口 → 后端读取该景点在数据库里已有的评论 → 情感分析模块逐条打分 → 返回各情感类别的统计结果和样例评论 → 前端用 ECharts 渲染饼图和柱状图。# app.py 中的核心路由示意 from flask import Flask, jsonify, request from analyzer import SentimentAnalyzer from db import get_reviews_by_scenic app Flask(__name__) analyzer SentimentAnalyzer() app.route(/api/analyze, methods[POST]) def analyze(): scenic_id request.json.get(scenic_id) reviews get_reviews_by_scenic(scenic_id) if not reviews: return jsonify({code: 404, msg: 该景点暂无评论数据请先爬取}) result analyzer.analyze_batch(reviews) return jsonify({code: 0, data: result})这个路由做的事情非常直白拿到前端传来的景点 ID去数据库捞评论丢给情感分析器批量处理最后把结构化结果返回。值得留意的是analyze_batch这个方法它不是简单地 for 循环而是内部先做文本清洗再逐条走分词和词表匹配最后按正面、负面、中性三个类别做聚合统计。2.3 依赖安装与启动顺序先后端后前端很多第一次跑这个项目的人翻车不是因为代码有问题而是启动顺序错了。后端 Flask 服务如果没起来前端页面能打开但所有接口全部报错页面看起来是“白屏控制台一片红”。正确的顺序是先装后端依赖启动 Python 服务再启动 Quasar 前端。# 第一步安装后端依赖Python 3.8 环境 pip install flask flask-cors requests jieba sqlalchemy pymysql # 第二步启动后端服务 python app.py # 第三步进入前端目录安装依赖并启动 cd frontend npm install npx quasar dev这套顺序里有几个参数需要按你自己的环境改数据库连接串在db.py里默认我拆包看到的是 MySQL 的配置如果你本地没装 MySQL可以直接改 SQLite改动量大约是 10 行Flask 的端口如果在app.run(port5000)被占用改成 5001 后前端quasar.conf.js里的 proxy 目标端口也要同步改否则代理转发会失效。启动完成后访问http://localhost:8080能看到登录注册页说明前端起来了再往后端http://localhost:5000/api/ping发一个 GET 请求返回 JSON 说明链路通了一半。3. 携程与马蜂窝评论爬虫请求伪装、分页抓取与字段落库3.1 两个平台的反爬差异与应对策略携程和马蜂窝的评论接口形态完全不一样。携程的评论数据走的是一套内部 API返回 JSON 结构里面嵌套了用户名、评分、评论内容、点评时间等字段但请求头里必须带特定的 Referer 和 User-Agent否则直接拒绝服务。马蜂窝则是服务端渲染的 HTML 页面评论数据混在 DOM 节点里需要先请求页面再用 XPath 或正则抽取而且它的列表页有滚动懒加载直接 requests 只能拿到前几条。这套系统的爬虫代码里对两个平台分别写了独立的爬虫类公共部分抽了一个 BaseCrawler里面封装了带重试的请求方法。核心思路就是把请求头伪装成真实浏览器携程接口用requests.get带 params 拉取马蜂窝用requests.get拿 HTML 后用lxml解析。# crawler_ctrip.py 的核心抓取逻辑节选 import requests from lxml import etree class CtripCrawler: def __init__(self): self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://you.ctrip.com/, Accept: application/json, } self.base_url https://you.ctrip.com/sight/{poi_id}/comment-list.html def fetch_comments(self, poi_id, pages5): all_comments [] for page in range(1, pages 1): resp requests.get( self.base_url.format(poi_idpoi_id), params{start: (page - 1) * 10}, headersself.headers, timeout10 ) data resp.json() items data.get(data, {}).get(commentList, []) for item in items: all_comments.append({ user: item.get(userName), content: item.get(content), score: item.get(score), time: item.get(commentTime) }) return all_comments这个实现里有几个参数是实际调试时最关键的点params里的start控制分页偏移携程每页固定 10 条timeout10是给每个请求的上限防止某个 IP 被限制后卡住整个爬虫poi_id是景点在携程内部的唯一 ID这个 ID 怎么找打开携程景点页URL 里sight/后面那串数字就是。3.2 马蜂窝的滚动加载问题与 XPath 字段抽取马蜂窝的评论列表页不是分页按钮而是“滚动到底部加载更多”这是典型的动态加载页面。直接 requests 拿到的 HTML 里只有第一屏的数据。这套系统里处理方式很务实分析页面里预埋的window.__INITIAL_STATE__全局变量这个变量里包含了前几页的评论数据够演示用了。如果要做大样本采集再考虑用 Selenium 模拟滚动但毕设场景下没必要引入那么重的依赖。# crawler_mafengwo.py 的 HTML 解析逻辑 import requests from lxml import etree import json import re class MafengwoCrawler: def __init__(self): self.headers { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X), Accept: text/html,application/xhtmlxml } def fetch_comments(self, scenic_id, pages3): url fhttps://www.mafengwo.cn/poi/{scenic_id}.html resp requests.get(url, headersself.headers, timeout10) html resp.text # 提取预埋在页面里的初始数据 match re.search(rwindow\.__INITIAL_STATE__\s*\s*(.*?)/script, html) if not match: return [] state json.loads(match.group(1)) comments state.get(commentList, []) return [{ user: c.get(nickname), content: c.get(comment), score: c.get(score, 0), time: c.get(date) } for c in comments]这里值得注意的细节是 User-Agent 用了 iPhone 的标识因为马蜂窝移动端的反爬策略比 PC 端宽松返回的 HTML 结构也更规整。re.search的正则里.*?是非贪婪匹配确保拿到第一个/script前的内容如果页面改版导致结构变化最先报错的就是这一行。整体看下来马蜂窝的爬虫实现比携程更简单但容错性也差一些后面第 5 章我会讲它最容易踩的坑。3.3 评论去重与字段清洗两个平台的评论字段并不一致马蜂窝没有评分字段或者评分为空携程的评论内容里可能带“发布于”之类的杂质文字。这套系统在写入数据库之前做了一层清洗和去重。清洗逻辑是去掉空白字符和 HTML 标签按长度过滤掉低于 5 个字的评论去重逻辑是对评论内容做 MD5 哈希存入数据库时加唯一索引重复评论直接忽略。采集字段最终落库的结构如下字段名类型说明idINT 自增主键scenic_nameVARCHAR(64)景点名称sourceVARCHAR(16)来源平台ctrip / mafengwouser_nameVARCHAR(64)用户昵称contentTEXT清洗后的评论文本scoreFLOAT评分携程有值马蜂窝可能为 0comment_timeDATETIME评论时间content_hashCHAR(32)内容 MD5用于去重这个表结构是这套系统里比较有价值的工程设计。content_hash字段配合数据库唯一索引能从源头杜绝重复数据污染后续的情感分析结果。4. 情感分析核心分词、词表打分与统计接口的实现思路4.1 jieba 分词与自定义词典的作用情感分析模块的第一步是分词。这套系统用的是 jieba 分词库默认词典对通用文本效果不错但旅游评论里有大量景点名、地名、口语化表达比如“迪士尼”“外滩”“打卡”默认分词经常会切成“迪斯 尼”或者“打 卡”影响后续情感词的匹配。# analyzer.py 中的分词与清洗 import jieba import re STOP_WORDS {的, 了, 是, 在, 就, 都, 也, 很, 有, 这, 那} def clean_and_tokenize(text): # 去 HTML 标签和特殊符号 text re.sub(r.*?, , text) text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9], , text) words jieba.lcut(text) # 过滤停用词和单字 return [w for w in words if w not in STOP_WORDS and len(w.strip()) 1]这里的核心参数是STOP_WORDS集合它直接决定情感分析的准确率。如果停用词太少“不算差”“不太行”里的“不”会被抽走变成正向情感停用词太多又会把“不错”里的“不”误杀。常见的做法是维护一个针对旅游评论场景的停用词表把“确实”“真的”“简直”这类程度副词保留下来——它们虽然在停用词名单里但在情感分析里起着语气强化作用一般不建议过滤。实战中我一般会把“没”“不”“别”这几个否定词单独处理因为它们的出现会让整句情感反转。4.2 正负向词表打分机制这套系统的情感判定没有用复杂的深度学习模型而是采用基于情感词典的极性打分。原理是构建一个正面词表和一个负面词表每个词带一个权重分词结果逐一匹配词表累计得分。得分大于阈值判定为正向小于负阈值判定为负向中间归为中性。# analyzer.py 的情感打分核心 class SentimentAnalyzer: def __init__(self): self.positive_words {好评: 2, 漂亮: 1.5, 值得: 1.5, 推荐: 2, 满意: 2} self.negative_words {差评: 2, 失望: 2, 一般: 0.5, 后悔: 2, 坑: 1.5} self.negation_words {不, 没, 别, 不太} def analyze_batch(self, reviews): results {positive: 0, negative: 0, neutral: 0} details [] for review in reviews: words clean_and_tokenize(review[content]) score 0 for idx, word in enumerate(words): if word in self.positive_words: score self.positive_words[word] elif word in self.negative_words: score - self.negative_words[word] # 否定词反转不 正面词 负面 if idx 0 and words[idx-1] in self.negation_words: score -score if score 0.5: results[positive] 1 details.append((review[content], 正面, score)) elif score -0.5: results[negative] 1 details.append((review[content], 负面, score)) else: results[neutral] 1 details.append((review[content], 中性, score)) return {stats: results, details: details}这里有个细节值得展开否定词反转用的是“看前一个词”的笨办法遇到“不太满意”这种双重否定就会判错——“不”和“太”都是否定词按这个逻辑会反转两次回到正面而实际语义是负面。要改进就把negation_words设计成窗口扫描统计一句话里否定词出现次数奇数次反转、偶数次不反转。但从毕设展示角度现有逻辑已经能覆盖绝大多数简单评论尤其是“景点不错”“服务很差”这类直白表达。4.3 统计结果如何喂给前端图表情感分析模块输出的stats字典包含了正、负、中三个类别的评论数量后端把这个字典连同样例评论一起打包成 JSON 返回。前端拿到数据后用 ECharts 的饼图展示情感分布用列表展示各情感类别下的代表性评论。这个接口的返回格式是一个约定前端result.html页面对应的渲染逻辑// frontend/src/pages/Result.vue 中的核心请求节选 async function loadResult() { const res await fetch(/api/analyze, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({ scenic_id: selectedScenicId }) }) const json await res.json() if (json.code 0) { renderChart(json.data.stats) renderCommentList(json.data.details) } }selectedScenicId是用户在前一个页面选中的景点 ID这个值从景点列表接口来。整条链路到这里就闭合了爬虫负责把外部评论搬进数据库情感分析模块把评论变成可量化的情感分数前端把分数变成图表。从工程实现角度看这套系统最有学习价值的地方不是某个算法有多牛而是数据完整地在三个层级之间流转没有断点。5. 避坑记录封 IP、跨域拦截与中文乱码四连5.1 爬虫请求频繁导致 IP 被封现象携程爬虫跑到第 30 页左右后续所有请求返回 403页面提示访问异常连手动打开网页都受限。原因请求频率太高没有设置延时携程的反爬机制识别到同一 IP 的密集访问后触发了临时封禁持续时间大约 10 到 20 分钟。解决在爬虫的fetch_comments里加入随机延时用time.sleep(random.uniform(1, 3))并且把每页的抓取间隔和总页数关联起来。另外把请求头里的Accept-Language补上降低被识别为脚本的概率。这套系统原始代码里没有延时我拆包时第一轮复现就是在这个位置踩的坑加延时后连续跑 200 页都没再触发封禁。5.2 前端页面打开后所有接口报跨域错误现象Quasar 前端跑在 8080 端口后端 Flask 跑在 5000 端口浏览器里请求/api/analyze直接报CORS error接口一个都调不通。原因这是一个标准的前后端分离跨域问题浏览器默认禁止不同端口之间的 XHR 请求。项目里虽然引了flask-cors但如果你没在 Flask 实例上正确注册 CORS跨域照样拦。解决后端的 Flask 入口里加一行配置# app.py 中的跨域配置 from flask_cors import CORS CORS(app, resources{r/*: {origins: *}})resources{r/*: {origins: *}}表示所有路由都允许任意来源访问毕设本地调试够用了。如果将来部署到服务器把origins改成前端的实际域名不要用通配符否则等于向后端开放了所有跨域权限。5.3 评论写入 MySQL 后中文乱码现象爬虫打印的评论内容是正常的但存进数据库后变成了???或者繁体乱码。原因MySQL 数据库表默认字符集是latin1不支持中文。Python 的 MySQL 连接串里也没指定charset参数导致写入时 UTF-8 编码被强制转成了 latin1。解决建表时指定字符集连接时也指定# db.py 中的连接串修改 engine create_engine( mysqlpymysql://root:passwordlocalhost:3306/travel_sentiment?charsetutf8mb4, echoTrue )charsetutf8mb4是关键参数utf8mb4是完整的 UTF-8 实现支持四字节的 emoji 字符。评论内容里经常有 emoji用utf8会报编码错误用utf8mb4才能完整存储。5.4 马蜂窝爬虫采到的评论数量明显比页面显示少现象页面上下拉能看到四五十条评论爬虫只抓到七八条。原因马蜂窝的window.__INITIAL_STATE__只预埋了前两页数据后续内容全部靠滚动触发接口动态加载而网络请求拿到的 HTML 里根本不含这些动态内容。解决接受现实——用预埋数据抓取就是只能抓到两页的量。如果确实需要更多直接在爬虫里再请求马蜂窝内部的评论分页接口但这个接口的签名参数是加密的调试成本比较高。毕设答辩场景下能抓到二三十条有效评论做情感分析展示就足够了重点是把清洗和分析链路讲清楚评审老师不会纠结样本量。6. 答辩前的最后一道工序换景点重跑全流程的检查清单这套系统默认带了几个演示景点但答辩时最出彩的动作是现场换一个完全没跑过的景点从爬虫到情感分析全链路走一遍。换景点不需要动代码只需要改一个参数景点 ID。携程的景点 ID 去景点页 URL 里找马蜂窝的景点 ID 去poi/后面的数字串里找。我自己的习惯是答辩前强制走一遍完整的验证流程按顺序确认五件事第一确认后端服务活着curl http://localhost:5000/api/ping返回值正常第二确认前端页面能打开登录后能看到景点列表第三执行一次新景点的爬虫确认数据库里新增了对应评论第四在前端页面选择这个新景点确认情感分布图和评论列表渲染出来了第五抽查三条评论人工判断情感倾向是否和分析结果一致。# 一键验证脚本 check_pipeline.sh #!/bin/bash # 检查后端 curl -s http://localhost:5000/api/ping | grep -q pong echo [OK] 后端存活 || echo [FAIL] 后端未启动 # 检查数据库评论数 mysql -uroot -p123456 -e SELECT COUNT(*) FROM reviews WHERE scenic_name东方明珠; # 检查情感分析接口 curl -s -X POST http://localhost:5000/api/analyze -H Content-Type: application/json -d {scenic_id:124} | python -m json.tool | head -20这个脚本看起来简单但实际能挡住大部分低级的答辩事故后端忘了启动、数据库没建表、景点 ID 选错导致接口返回空数据。每次换新景点我会先把要换的 ID 记下来跑完爬虫后立刻查一下数据库的写入量确认没有因为反爬 IP 封禁导致零数据入库。还有一个细节容易被忽略爬虫抓下来的是实时评论但情感分析的结果只对当前数据库里的数据有意义。答辩前不要反复跑同一个景点的爬虫因为重复评论会被content_hash唯一索引拦掉但接口的响应时间会变长——评审老师看到页面转圈超过三秒整个演示的流畅度就打折了。从那以后我每次做类似的项目演示都会强制把“换数据源重跑全链路”这一步放进检查清单先确认数据能进来再确认分析能算完最后才点开前端页面。这套流程看着笨但能帮你筛掉八成现场翻车的情况。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Rhino产品造型设计AI实战:从NURBS控制点到生成式工作流 2026/10/1 9:24:59

Rhino产品造型设计AI实战:从NURBS控制点到生成式工作流

简介:这份文档面向产品设计专业学生、Rhino使用者及关注AI辅助设计的从业者,系统梳理人工智能技术在Rhino产品造型设计中的应用路径与创新实践,帮助读者理解从概念草图到工程图输出的智能化设计流程。资源包内含1个docx文档,约110…

阅读更多 →
改进YOLOv8实现枣子图像分割:RepHGNetV2与AFPN-P345实战 2026/10/1 9:24:53

改进YOLOv8实现枣子图像分割:RepHGNetV2与AFPN-P345实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Debian 11换国内源:APT信任链重建与安全更新配置指南 2026/10/1 9:24:53

Debian 11换国内源:APT信任链重建与安全更新配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
智能手表晶振选型指南:从低功耗矛盾到布局调试实战 2026/10/1 9:24:53

智能手表晶振选型指南:从低功耗矛盾到布局调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
视觉引导拆垛为何必须用3D相机?工业落地避坑指南 2026/10/1 9:24:52

视觉引导拆垛为何必须用3D相机?工业落地避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
PICORV32软核源码精读:从状态机到总线握手与配置裁剪 2026/10/1 9:24:52

PICORV32软核源码精读:从状态机到总线握手与配置裁剪

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉