☰
基于Django+Vue的旅游景点评论情感分析源码实战:环境搭建、避坑与进阶优化
2026/9/26 22:07:33 网站建设 项目流程

简介:这份资源是面向高校学生与Python开发初学者的旅游景点评论情感分析完整项目,采用Python+Django后端与Vue前端技术栈,可直接用于毕业设计、期末大作业或课程设计场景。项目代码注释齐全,新手也能读懂,部署流程简单,下载后即可运行使用。压缩包共102个文件,约47.72MB,其中32个py文件承载Django后端业务逻辑与情感分析核心算法,10个vue文件与6个js文件构成前端交互界面,另有json配置、html模板、scss样式及png图片等资源,目录结构清晰,便于按模块查阅与二次开发。目前已有190人学习下载,项目经过严格调试,功能完善、界面美观、操作便捷,配套文档说明帮助读者快速理解系统架构与实现思路,适合需要高分项目参考或希望掌握文本情感分析实战流程的学习者。

1. 从一堆景区评论里挖出情绪:这套 Django+Vue 源码到底能跑出什么

做旅游平台运营或者带毕业设计的朋友,大概率都遇到过这种场景:后台攒了几万条景点评论,人工翻到眼瞎也看不出游客到底在骂什么、夸什么。这套基于 Python+Django+Vue 的旅游景点评论情感分析源码,解决的就是这件事——把非结构化的中文评论喂进去,自动判出正面、负面、中性,再按景点、时间维度聚合成可视化看板。它不是玩具 demo,前端 Vue 负责图表交互,后端 Django 扛住数据接口和模型调度,情感分析模块通常走 SnowNLP 或朴素贝叶斯这类轻量方案,适合毕业设计、期末大作业,也适合中小型旅游平台做舆情初筛。如果你正被“评论太多、情绪看不清”卡住,这套东西值得拆开跑一遍。

2. 环境搭建与依赖安装:把 Django 和 Vue 两条腿都立起来

这套项目是前后端分离结构,Python 侧跑 Django 提供 API,Node 侧跑 Vue 做页面渲染。很多人第一次跑翻车,不是代码有问题,而是两条腿的环境没对齐。下面按我实际复现的顺序拆。

2.1 Python 侧:虚拟环境与 Django 依赖

先确认 Python 版本。Django 2.x 和 3.x 对 Python 要求不同,源码里requirements.txt一般会锁版本,别自作主张升到最新。我一般用 3.8 到 3.10 之间,兼容性最稳。

# 创建虚拟环境,避免污染全局包 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 安装依赖,requirements.txt 在项目根目录 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

这里几个参数值得说清楚。-i指定清华镜像源,国内下载 Django、jieba、SnowNLP 这类包会快很多,不然卡在Collecting django能等到怀疑人生。requirements.txt里通常包含 Django、djangorestframework、pymysql、jieba、snownlp、pandas 这几类,如果缺了pymysql,Django 连 MySQL 时会直接报No module named 'MySQLdb',这是血泪经验里出现频率最高的一个。

数据库配置在settings.py的DATABASES段。源码默认可能用 SQLite,也可能用 MySQL。用 MySQL 的话,先建库:

CREATE DATABASE tourism_sentiment DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

字符集必须utf8mb4,因为评论里 emoji 和生僻字不少,用utf8会在写入时丢字符或者直接报错。建完库回到settings.py改NAME、USER、PASSWORD、HOST、PORT,然后在__init__.py里加两行:

import pymysql pymysql.install_as_MySQLdb()

这两行的作用是让 Django 把 pymysql 当成 MySQLdb 来用,不加的话迁移阶段就崩。接着执行迁移:

python manage.py makemigrations python manage.py migrate python manage.py runserver

makemigrations根据模型生成迁移文件,migrate把表真正建到库里。如果报Table 'xxx' doesn't exist,八成是迁移没跑或者跑到了别的库。跑起来后访问http://127.0.0.1:8000,能看到 Django 欢迎页说明后端通了。

2.2 Vue 侧:Node 版本与依赖安装

前端在frontend或vue目录下,先看package.json里的vue版本。Vue 2 和 Vue 3 的构建工具不同,Vue 2 多用 vue-cli,Vue 3 多用 Vite。别混着来。

# 进入前端目录 cd frontend # 安装依赖,用淘宝源加速 npm install --registry=https://registry.npmmirror.com # 启动开发服务器 npm run serve

npm run serve是 Vue 2 项目的常见命令,Vue 3 + Vite 则是npm run dev。跑起来后默认在http://localhost:8080或http://localhost:5173。前端要调后端接口,接口地址一般写在.env文件或者src/utils/request.js里,确认baseURL指向http://127.0.0.1:8000,否则页面能打开但图表全是空的。

跨域是另一个高频坑。Django 侧要装django-cors-headers,在INSTALLED_APPS里注册,中间件加CorsMiddleware,再配CORS_ALLOW_ALL_ORIGINS = True(开发阶段)。不配的话浏览器控制台会刷一片CORS policy红字,接口请求全被拦。

2.3 前后端联调的最小验证路径

环境都起来后,别急着点页面。先单独验证后端接口:

# 用 curl 测一个评论列表接口 curl http://127.0.0.1:8000/api/comments/?page=1

返回 JSON 说明后端 OK。再打开前端页面,F12 看 Network 面板,如果接口返回 200 但页面没数据,多半是前端字段名和后端对不上,比如后端返回sentiment_score,前端读的是score。这种字段错位在二手源码里很常见,对着 Network 里的响应体改前端取值就行。

3. 情感分析核心链路:从原始评论到情绪标签

环境通了只是第一步,真正决定这套源码值不值得用的是情感分析这条链路。它一般分四段:数据采集入库、中文分词与清洗、情感打分、结果聚合展示。每一段都有能翻车的地方。

3.1 评论数据入库与字段设计

评论表通常长这样:id、scenic_name、content、user_name、publish_time、sentiment、score。content字段必须用TextField,别用CharField,评论超过 255 字就截断了。sentiment存标签,score存 0 到 1 的置信度。

如果源码带爬虫模块,一般在spider或crawler目录下,用 requests + BeautifulSoup 或者 scrapy。跑爬虫前先看目标站点有没有反爬,加User-Agent和time.sleep是基本操作。我一般会先手动灌几条测试数据,确认分析链路通了再上量:

# 手动插入测试评论,验证链路 from app.models import Comment Comment.objects.create( scenic_name="西湖", content="风景很美,但是人太多了,排队两小时", user_name="test_user" )

这条评论故意做成“褒贬混合”,用来测模型能不能给出中性或偏正面的判断。纯正面和纯负面的样本太容易过,混合样本才是照妖镜。

3.2 中文分词与停用词处理

中文情感分析绕不开分词。源码里大概率用 jieba。核心逻辑是先去掉标点、数字、英文,再用停用词表过滤“的、了、是、在”这类无意义词。

import jieba import re def clean_text(text): # 去掉非中文、非字母数字的字符 text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', '', text) # 分词 words = jieba.lcut(text) # 加载停用词表 with open('stopwords.txt', 'r', encoding='utf-8') as f: stopwords = set(f.read().splitlines()) # 过滤停用词和单字 words = [w for w in words if w not in stopwords and len(w) > 1] return words

re.sub那行的正则[^\u4e00-\u9fa5a-zA-Z0-9]意思是保留中文、字母、数字,其余全删。\u4e00-\u9fa5是中文 Unicode 范围。停用词表如果源码没带,去 GitHub 搜“中文停用词表”下一个,几百行就够用。len(w) > 1过滤单字,因为单字在情感判断里噪声太大,“好”和“不好”里的“好”单独拎出来会误判。

分词完一般还要做词性过滤,只保留形容词、动词、副词这类带情感倾向的词。jieba 的posseg模块能做:

import jieba.posseg as pseg def filter_by_pos(words): allowed = {'a', 'ad', 'an', 'v', 'vd', 'd'} # 形容词、动词、副词 result = [] for word, flag in pseg.cut(''.join(words)): if flag in allowed: result.append(word) return result

a是形容词,v是动词,d是副词。这一步能明显提升准确率,尤其是评论里“不推荐”“超级棒”这种带程度副词的表达。

3.3 情感打分:SnowNLP 与朴素贝叶斯的取舍

源码里最常见的是 SnowNLP,一行代码出分数:

from snownlp import SnowNLP def get_sentiment(text): s = SnowNLP(text) score = s.sentiments # 0 到 1,越接近 1 越正面 if score > 0.6: return 'positive', score elif score < 0.4: return 'negative', score else: return 'neutral', score

sentiments返回的是正面概率。阈值 0.6 和 0.4 是我常用的分界,源码里可能用 0.5 一刀切,但那样中性评论会被硬分到两边。调阈值的时候拿几十条人工标注过的评论跑一遍,看准确率再定。

SnowNLP 的优点是开箱即用,缺点是它基于电商评论语料训练,对旅游场景的“景色绝了”“排队排到崩溃”这类表达不一定准。如果源码用的是朴素贝叶斯,那通常会带一个训练脚本:

from sklearn.naive_bayes import MultinomialNB from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.pipeline import Pipeline # 构建 pipeline:TF-IDF 向量化 + 朴素贝叶斯 model = Pipeline([ ('tfidf', TfidfVectorizer(tokenizer=lambda x: x, preprocessor=None)), ('clf', MultinomialNB()) ]) # X_train 是分词后的列表,y_train 是标签 model.fit(X_train, y_train)

这里tokenizer=lambda x: x是因为传入的已经是分好词的列表,不需要再分。TfidfVectorizer把词转成权重向量,MultinomialNB做分类。训练集一般要几千条标注数据,源码如果带了train.csv就直接用,没带的话得自己标一批,这是最耗时的部分。

3.4 结果聚合与接口输出

分析完的标签要聚合才能看出价值。常见做法是按景点分组算正面率:

from django.db.models import Count, Q def scenic_sentiment_stats(): return Comment.objects.values('scenic_name').annotate( total=Count('id'), positive=Count('id', filter=Q(sentiment='positive')), negative=Count('id', filter=Q(sentiment='negative')), neutral=Count('id', filter=Q(sentiment='neutral')) ).order_by('-total')

values按景点分组,annotate配合Count和Q对象做条件计数。这个查询直接返回每个景点的评论总数和三类情绪数量,前端拿去做柱状图或饼图。如果数据量大,记得给scenic_name和sentiment加索引,不然几万条数据聚合会慢到接口超时。

4. 避坑与常见问题排查:那些让项目跑不起来的细节

这套源码在二手流转里被改过很多遍,环境、依赖、字段对不上的情况太常见。下面几条是我复现时真实踩过的,按“现象 → 原因 → 解决”列出来。

4.1 迁移报错No module named 'MySQLdb'

现象:执行python manage.py migrate时直接抛ImportError: No module named 'MySQLdb'。

原因:Django 默认用 MySQLdb 连 MySQL,但 MySQLdb 在 Python 3 下安装麻烦,源码里通常用 pymysql 替代,只是没在__init__.py里注册。

解决:在项目同名目录的__init__.py里加import pymysql和pymysql.install_as_MySQLdb(),再确认requirements.txt里有 pymysql。

4.2 前端页面能开但图表空白

现象:Vue 页面正常渲染,但所有图表区域是空的,Network 面板里接口请求返回 200 但数据为空或 404。

原因:接口baseURL没配对,或者后端接口路径和前端请求路径不一致。二手源码里前端可能还指着原作者的服务器地址。

解决:打开src/utils/request.js或.env文件,把baseURL改成http://127.0.0.1:8000。再对着 Network 里的请求 URL 和后端urls.py里的路由逐条核对,路径差一个斜杠都会 404。

4.3 情感分析结果全是中性

现象:跑完分析,所有评论的sentiment都是neutral,正面负面一条没有。

原因:SnowNLP 的sentiments对短文本或者带大量网络用语的评论不敏感,分数集中在 0.5 附近;或者阈值设得太宽,0.4 到 0.6 之间全归中性。

解决:先打印几条评论的原始score看分布,如果确实集中在 0.5 附近,把阈值收窄到 0.55 和 0.45,或者换用带训练集的朴素贝叶斯方案。另外检查分词是不是把情感词过滤掉了,停用词表别把“不”“很”“太”这类程度词也删了。

4.4 中文乱码或 emoji 丢失

现象:评论入库后中文变成问号,或者 emoji 直接消失。

原因:数据库字符集不是utf8mb4,或者 Django 连接配置里没指定字符集。

解决:建库时用CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,settings.py的DATABASES里加'OPTIONS': {'charset': 'utf8mb4'}。已经建好的库用ALTER DATABASE改字符集,表也要单独改。

4.5npm install卡住或报 node-sass 编译错误

现象:npm install跑几分钟不动,或者报node-sass相关编译错误。

原因:node-sass 对 Node 版本极其敏感,Node 16 以上经常编不过;或者默认源太慢。

解决:换npm install --registry=https://registry.npmmirror.com。如果还报 node-sass,把package.json里的node-sass换成sass(dart-sass),前者已经废弃。Node 版本用 nvm 切到 14 或 16,别用最新的 20+。

5. 进阶玩法:把情感分析从“能跑”推到“能用”

跑通只是及格线。这套源码真正能拿出手,得在准确率和展示维度上再推一把。下面几个方向是我实际改过的,投入产出比高。

5.1 用领域词典修正 SnowNLP 的偏差

SnowNLP 在旅游场景下会把“排队三小时”判成中性,因为它没学过这种表达。解决办法是挂一个领域情感词典,在 SnowNLP 打分后做二次修正:

# 自定义旅游领域情感词权重 domain_dict = { '排队': -0.3, '人山人海': -0.2, '宰客': -0.5, '出片': 0.3, '绝美': 0.4, '值得': 0.3, '坑': -0.4 } def enhanced_sentiment(text): base = SnowNLP(text).sentiments for word, weight in domain_dict.items(): if word in text: base += weight # 夹到 0 到 1 之间 base = max(0.0, min(1.0, base)) if base > 0.6: return 'positive', base elif base < 0.4: return 'negative', base return 'neutral', base

domain_dict里的权重是我根据几百条评论试出来的,你可以按自己的数据调。max(0.0, min(1.0, base))保证分数不越界。这一步能把“风景绝美但排队排到崩溃”这种混合评论判得更合理。

5.2 按时间维度做情绪趋势

单看总量不够,运营更关心情绪随时间的变化。给评论表加个按天聚合的接口:

from django.db.models.functions import TruncDate def daily_trend(scenic_name): return Comment.objects.filter(scenic_name=scenic_name).annotate( day=TruncDate('publish_time') ).values('day').annotate( avg_score=Avg('score'), count=Count('id') ).order_by('day')

TruncDate把时间戳截断到天,Avg('score')算每天的平均情感分。前端拿这个数据画折线图,能看出某个景点是不是在某个时间段口碑突然下滑。如果publish_time是字符串类型,先改成DateTimeField,不然TruncDate不认。

5.3 验证分析效果:人工抽检与混淆矩阵

别信模型跑出来的数字,自己抽 50 条评论人工标一遍,和模型结果对比:

from sklearn.metrics import classification_report # y_true 是人工标注,y_pred 是模型输出 print(classification_report(y_true, y_pred, labels=['positive', 'neutral', 'negative']))

classification_report会输出每个类别的精确率、召回率和 F1。如果neutral的召回率特别低,说明阈值还是太宽,中性评论被误分到了两边。我一般会把 F1 低于 0.6 的类别拎出来,针对性补训练数据或者调词典权重。

5.4 一个具体技巧:把分析结果缓存起来

情感分析是计算密集型操作,每次刷新页面都重跑一遍,几万条评论能把接口拖死。用 Django 的缓存框架把结果存起来:

from django.core.cache import cache def get_cached_sentiment(comment_id, text): key = f'sentiment_{comment_id}' result = cache.get(key) if result is None: result = enhanced_sentiment(text) cache.set(key, result, 60 * 60 * 24) # 缓存一天 return result

cache.get先查缓存,没有才跑分析,cache.set的第三个参数是过期时间,单位秒。开发阶段用内存缓存就行,settings.py里配CACHES用LocMemCache。上线换 Redis,几万条评论的重复查询基本秒回。

从那以后我每次拿到这类二手源码,都强制先跑一遍最小验证链路——插一条测试评论、调一次接口、看一次前端渲染——确认三端通了再动业务代码。这套旅游景点评论情感分析源码的骨架是完整的,Django 扛接口、Vue 做展示、SnowNLP 或朴素贝叶斯出标签,把环境对齐、字段对齐、阈值调对,它就能从“能跑”变成“能用”。希望帮到你。

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

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

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

立即咨询