资讯动态

基于Python的旅游评论情感分析系统实战:从爬虫到可视化

发布时间:2026/8/31 8:10:10 来源:尧图企业网站定制
简介这是一套面向数据分析初学者与旅游行业数字化实践者的Python评论分析系统聚焦真实场景下的游客情感挖掘与舆情洞察解决景点口碑量化评估、高频关键词提取及用户反馈归因等实际问题。资源包共102个文件含32个核心Python脚本覆盖数据爬取、清洗、LDA主题建模、情感分析与可视化、10个Vue前端页面实现评论词云、评分分布、时间趋势等交互图表、6个PNG图标与SVG素材以及配套JSON配置、HTML模板和SCSS样式文件整体压缩包47.7MB结构清晰前后端分离设计便于二次开发与本地部署。已有675人学习下载提供完整可运行工程包含从原始评论采集到分析结果展示的全流程代码、预置测试数据集、Quasar框架搭建的轻量级Web界面以及.editorconfig、.gitignore等标准化开发配置适合快速上手实战项目并拓展至其他垂直领域评论分析任务。 搞旅游景点评论分析这件事我一开始其实是冲着“想看看大众对某地到底什么印象”这个朴素需求去的。结果做着做着发现这压根不只是一个爬虫脚本的事而是一整套标准的Python自然语言处理流程数据采集、文本清洗、分词、情感打分、可视化呈现。如果你正想找个Python项目练手又不想写那种烂大街的博客系统或学生管理系统这个项目会非常适合你——它把爬虫、Pandas数据处理、jieba分词、情感分析、Pyecharts可视化全串起来了而且做完之后你能直接看到一张张图告诉你“游客到底满意不满意”成就感非常直观。这篇文章我会完整走一遍这个系统的落地全过程从技术选型的原因、每一步实现的细节、到我实际踩过的坑全部交代清楚。不管你是想交课程设计还是真想做一个能用的评论分析小工具都能直接照着复现。1. 项目概述与整体设计思路1.1 为什么要做评论分析而不是只看评分很多人觉得看一个景点好不好直接看平台评分不就行了4.8分就是好3.5分就是一般。但这里有个特别大的问题评分只告诉你结果不告诉你原因。举个例子某个古镇景区评分4.6看起来很高但你翻评论会发现大量商业化太严重全是卖东西的进去就想出来。而另一个评分4.5的景区评论区是风景原生态人少安静适合发呆。这两个景区体验完全不同但只看分数你根本区分不出来。评论分析系统要解决的就是这个问题——把非结构化的文本评论转成结构化的情感标签和主题分布让你知道好评到底好在哪里差评到底差在哪里。这个系统本质上就是一个典型的文本挖掘项目。核心流程就是采集数据 - 清洗文本 - 分词 - 情感判定 - 统计分析 - 可视化展示。1.2 技术选型为什么是Python全家桶选Python做这个项目不是因为它简单而是因为整个NLP生态在Python里太成熟了你几乎不用造任何轮子Requests BeautifulSoup4采集网页数据足以应对大部分静态评论页面Pandas处理评论数据的各种清洗、去重、聚合操作比纯Python手写高效太多Jieba中文分词的标准库支持自定义词典和词性标注对付评论这种短文本足够了SnowNLP开箱即用的中文情感分析库内置了训练好的情感模型虽然准确率不是顶级但做Demo和入门非常合适Pyecharts / WordCloud数据可视化能把统计结果变成直观的图表有人可能会问情感分析为什么不用深度学习模型比如BERT这里我要说句实在话在评论分析这种场景下深度模型效果确实更好但它需要标注数据、GPU训练、部署环境的成本也高。对于一个个人项目或者课程设计来说基于情感词典和规则的方案加上SnowNLP辅助已经完全够用了。我后面会详细讲这个选型逻辑。1.3 系统整体架构设计整个系统我分成五个模块模块之间用函数解耦方便单独调试数据采集模块 - 数据清洗模块 - 情感分析模块 - 统计聚合模块 - 可视化模块每个模块只做一件事。比如采集模块只负责把评论和评分存成CSV清洗模块只负责把脏数据变成干净的文本列表情感分析模块只负责给每条评论打分。这样做的好处是以后你想换数据源比如从携程换成美团只需要改采集模块后面的代码完全不用动。2. 数据准备怎么拿到靠谱的景点评论2.1 数据合规问题必须先说清楚在动手写爬虫之前有件事必须摆到台面上讲爬取数据要遵守网站的robots协议和用户协议。很多旅游平台的评论数据是受保护的明文爬取可能涉及法律风险。我的建议是做项目学习时优先选择以下渠道使用平台公开的API如果目标平台开放了开发者接口优先用API。比如一些旅游平台有开放数据接口。使用公开数据集GitHub、和鲸社区、天池等平台上有现成的旅游评论数据集拿来直接做分析完全没有问题。自己采集时选公开页面、控制频率如果坚持自己采集请遵守网站的访问规则设置合理的时间间隔不要对目标服务器造成压力。只采集公开信息不碰需要登录才能看到的数据。我在项目开发阶段用的是公开数据集加上自己手动备份的一些评论数据先把整个分析流程跑通再去考虑要不要扩展数据源。2.2 数据清洗评论数据究竟有多脏拿到原始评论数据后千万不要急着做分析。真实世界的评论数据脏得让你怀疑人生。我总结了几类常见的问题乱码和特殊符号评论里经常有各种特殊字符比如emoji、颜文字、HTML标签残留、多余的换行符。这些对分词和情感分析都是干扰项需要统一清洗。重复评论有些用户会刷屏或者平台上本身就有系统自动评论。Pandas的drop_duplicates()可以直接处理但要注意按评论内容去重而不是按ID。短评论比如好不错666这种一两个字的评论对情感统计没有什么价值一般会过滤掉。我通常设置一个最小长度阈值少于4个字符的评论直接丢弃。评论文本和评分不匹配这是最麻烦的。比如用户给了五星但评论内容是堵车堵到怀疑人生景区门口排队两小时。这种数据需要人工抽检判断或者在统计时分开处理——评分和文本情感分开统计。2.3 数据格式设计我建议把清洗后的数据保存成CSV格式字段如下字段名类型说明comment_idstr评论唯一IDspot_namestr景点名称comment_timestr评论时间ratingfloat用户评分1-5comment_textstr清洗后的评论文本user_namestr用户名可脱敏这里有一个小的设计考虑评论时间和评分虽然不直接参与情感分析但在后面的统计聚合中很有用。比如你可以分析不同季节的情感倾向以及评分和情感得分是否一致。所以先存原始数据后处理逻辑不要上来就丢字段。3. 文本预处理与分词让机器读懂中文评论3.1 中文分词的难点中文和英文不一样英文单词之间靠空格天然隔开中文没有这个分隔符。这地方真漂亮到底应该切分成这/地方/真/漂亮还是这地/方真/漂亮这就是分词要解决的问题。Jieba分词是目前最常用的中文分词工具。它基于统计词典和HMM模型能在一定程度上识别新词和未登录词。对旅游评论这个场景我强烈建议你加载自定义词典把景点名、地名、特色美食、当地俗语加进去这样分词效果会有质的提升。举个例子我在分析某古镇评论时扎肉当地特色菜总被切成扎/肉导致情感分析完全不准确。把扎肉加进自定义词典后分词结果变成了扎肉后续情感打分就正常了。3.2 一个标准的分词处理流程我的处理流程是四个步骤import jieba import re import pandas as pd def clean_text(text): # 去除HTML标签 text re.sub(r.*?, , text) # 去除URL text re.sub(rhttp\S, , text) # 去除特殊符号和数字 text re.sub(r[^\u4e00-\u9fa5a-zA-Z], , text) # 去除多余空白 text re.sub(r\s, , text).strip() return text def tokenize(text): # 加载自定义词典 jieba.load_userdict(spot_dict.txt) words jieba.lcut(text) # 过滤单字词和停用词 stopwords set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: stopwords.add(line.strip()) words [w for w in words if len(w) 1 and w not in stopwords] return words这里有几个细节值得说为什么要过滤单字词中文里单字词的语义通常不完整而且大部分是语气助词或虚词比如的了啊。把它们过滤掉能降低噪音。为什么要用停用词表网上有现成的中文停用词表比如哈工大停用词表、百度停用词表下载一个直接用。不过还是要根据你的数据情况手动补充比如我们他们这个那个这类词在评论里很常见但对分析没帮助。自定义词典的格式Jieba的自定义词典每行是一个词格式是词语 词频 词性词频和词性可以省略。比如扎肉 100 n或者直接扎肉。3.3 词云生成一眼看出大家在聊什么分词之后最直观的分析手段就是词云。词云能快速告诉你评论里出现频率最高的词是什么整体评价方向是什么。from wordcloud import WordCloud import matplotlib.pyplot as plt def generate_wordcloud(word_freq_dict, output_path): wc WordCloud( font_pathmsyh.ttc, # 重要必须指定中文字体否则显示乱码 width800, height600, background_colorwhite, max_words100 ) wc.generate_from_frequencies(word_freq_dict) plt.figure(figsize(10, 8)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(output_path, dpi150)这段代码里最关键的就是font_path参数。WordCloud默认不支持中文如果不指定中文字体文件生成的词云全是方块。Windows系统一般用msyh.ttc微软雅黑Mac用/System/Library/Fonts/PingFang.ttc。这个坑我踩过一次折腾了半小时才发现是字体问题。3.4 词频统计的常见误区做词频统计的时候很多人直接对分词结果做Counter然后取前20个词结果发现全是景点旅游地方景区这种泛泛的词。这是因为你没有过滤掉领域无关的通用词。我的做法是维护一个领域停用词表把这个什么感觉地方东西这类词加进去。另外还可以考虑用TF-IDF而非单纯词频这样能突出每个景点评论中的特征词。不过如果你的分析是面向多个景点的横向对比那就用TF-IDF如果只分析单个景点单纯词频加上停用词过滤就够用了。4. 情感分析实现判断评论是夸还是骂4.1 情感分析的技术路线选择情感分析有三种主流实现路线我帮你梳理清楚再选择方案优点缺点适用场景基于情感词典可解释性强、无需训练数据、速度快对否定句、双重否定处理差、需要人工维护词典通用评论情感分析、快速原型基于机器学习朴素贝叶斯、SVM准确率比词典法高需要大量标注数据、需要特征工程有历史标注数据的场景基于深度学习LSTM、BERT准确率最高、能处理复杂语义需要GPU、训练时间成本高、需要标注数据工业级应用、研究项目对于这个项目我建议直接使用SnowNLP 情感词典混合方案。SnowNLP内部是基于朴素贝叶斯训练的训练语料主要是电商购物评论直接用在旅游场景上会有偏差。但它依然是一个很好的baseline我后面会讲怎么弥补它的不足。4.2 SnowNLP基础用法SnowNLP的使用非常简单from snownlp import SnowNLP text 风景很美但人太多了排队排了一个小时 s SnowNLP(text) print(s.sentiments) # 输出0到1之间的情感得分sentiments返回的是一个0到1之间的浮点数越接近1表示越正面越接近0表示越负面0.5是中性立场。比如上面对风景很美但人太多了这句话SnowNLP可能给出的得分是0.6左右勉强偏正面。但仔细想想这句话其实是很明显的中性偏负面评价。这就是预训练模型在特定场景下的盲区。4.3 情感词典自定义与打分逻辑为了弥补SnowNLP的不足我需要叠加一个基于情感词典的规则打分器。核心思路是准备一份中文情感词典包含正面词和负面词每个词带权重对评论分词后对每个词查情感词典累加情感得分使用否定词表处理不没难等否定词如果否定词出现在情感词前面情感分值反转使用程度副词表非常特别有点稍微调节权重# 简化版情感打分逻辑 positive_words {漂亮: 2, 好: 1, 值得: 1.5, 干净: 1, 热情: 1.5} negative_words {差: -2, 脏: -1.5, 坑: -2, 失望: -2, 贵: -1} negation_words {不, 没, 无, 非} degree_adverbs {非常: 2, 特别: 2, 有点: 0.5, 稍微: 0.7, 很: 1.5} def sentiment_score(words): score 0 i 0 while i len(words): word words[i] # 检查是否有否定词在情感词前 if i 0 and words[i-1] in negation_words: if word in positive_words: score - positive_words[word] elif word in negative_words: score - negative_words[word] # 检查程度副词 elif i 0 and words[i-1] in degree_adverbs: weight degree_adverbs[words[i-1]] if word in positive_words: score positive_words[word] * weight elif word in negative_words: score negative_words[word] * weight else: if word in positive_words: score positive_words[word] elif word in negative_words: score negative_words[word] i 1 return score这个逻辑是简化版实际用的时候还要处理更复杂的情况。比如不怎么样这种表达单独靠词典法也是识别不出来的。所以最终我采取的策略是SnowNLP得分和词典法得分加权融合。def final_sentiment(text, words): s SnowNLP(text) snownlp_score s.sentiments # 0到1转成-1到1 snownlp_score (snownlp_score - 0.5) * 2 dict_score sentiment_score(words) # 归一化词典得分到-1到1 dict_score max(-1, min(1, dict_score / 5)) # 加权融合 final_score 0.6 * snownlp_score 0.4 * dict_score return final_score4.4 情感词典的制作思路网上能下载到现成的中文情感词典比较好用的有知网HowNet情感词典、大连理工大学情感词汇本体库。这些词典的词量都很大但直接拿来用效果不一定好因为不同场景下的情感词权重不一样。我的建议是在自己的语料上先跑一遍分词和词频统计人工挑出出现频率高、情感倾向明显的词建立一个项目专属的情感词典。这活儿听起来麻烦但实际上就是一个不断迭代的过程。先用通用词典分析一遍看哪些评论的判定结果明显不对再针对性地往词典里加词、调权重。我大概迭代了三四轮情感判定的准确率就从刚过60%提到了80%左右。5. 统计聚合与可视化把分析结果变成决策依据5.1 情感分布统计情感分析的核心输出就是每条评论一个情感分值。接下来要做的是把这些分值聚合起来统计正面、中性、负面评论的占比。def classify_sentiment(score): if score 0.1: return 正面 elif score -0.1: return 负面 else: return 中性 df[sentiment_score] df[comment_text].apply(lambda x: final_sentiment(x, tokenize(x))) df[sentiment_label] df[sentiment_score].apply(classify_sentiment)这里为什么阈值设为0.1和-0.1而不是0因为情感分值的分布通常是连续的完全等于0的评论很少。如果阈值设成0很容易把一些模棱两可的评论误判为中性。稍微拉大中性区间可以让分类结果更稳定也更能反映真实的中间态度——很多人对景点就是还行没什么特别的感觉。5.2 可视化方案Pyecharts实战Pyecharts是Python下交互式图表库生成的图表是HTML格式美观且支持缩放、鼠标悬浮查看数据。我用它做了四张图饼图情感占比from pyecharts.charts import Pie from pyecharts import options as opts def plot_sentiment_pie(df): sentiment_counts df[sentiment_label].value_counts() pie ( Pie() .add(, [list(z) for z in zip(sentiment_counts.index, sentiment_counts.values)]) .set_global_opts(title_optsopts.TitleOpts(title情感倾向分布)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {c} ({d}%))) ) return pie柱状图不同评分对应的平均情感分这页图特别有意思可以看到用户的显式评分和隐性情感是否一致。如果出现4星评分但是情感得分很低的评论说明用户可能因为某些原因比如性价比还可以但体验不好给了高分。这类评论往往是深入了解用户真实感受的关键。词云正面评论vs负面评论的关键词对比把情感标签为正面的评论集合成一个大文本做词云再把负面的评论也做成词云两个一比差异一目了然。正面词云往往是漂亮值得方便热情负面词云往往是排队贵脏失望。时间趋势图情感得分的时间变化按月份对情感得分做平均可以看出来景区的服务质量是不是在波动。比如某景区6月份情感得分突然暴跌后来发现是因为暑假门票涨价了引发的差评潮。5.3 综合指数计算除了分维度可视化我还计算了一个综合评分指数公式如下综合指数 0.3 * 平均评分 / 5 0.7 * 平均情感得分这个设计的逻辑是平均评分反映用户的显式态度平均情感得分反映用户的隐性态度。用户打分受到很多非体验因素影响比如来都来了心理、平台返现而评论文本的情感分析往往更真实。权重上更偏重情感得分也是基于这个考虑。6. 常见问题与排查经验实录6.1 Jieba分词效果差怎么办现象分词结果出现大量意义不明的碎片比如真的觉得然后这种语气词没有被过滤干净。排查思路检查停用词表是否完整特别是真的这种口语高频词检查自定义词典有没有把专有名词加进去尝试用jieba.enable_paddle()启动Paddle模式分词效果会提升但速度会慢一些我最终是把停用词表扩充到了1000多个词覆盖了口语化的连接词、语气词、代词分词噪音才明显降下来。6.2 SnowNLP对旅游评论判定偏差大现象风景不错住宿太贵SnowNLP给出的得分为0.7明显偏高。因为SnowNLP的训练语料以电商购物为主太贵在电商语境下有时是中性描述。而旅游场景下太贵往往是负面信号。解决方案使用自定义训练语料重新训练SnowNLP。SnowNLP支持train()方法你可以自己标注2000条旅游评论进行训练。或者像我一样把SnowNLP当作一个子模型叠加情感词典打分器来纠正偏差。6.3 词云生成乱码和空白现象词云生成后全是方块或者词云图空白一片。排查思路方块问题font_path没有指定中文字体在创建WordCloud时加font_pathmsyh.ttc空白问题传给generate_from_frequencies的字典可能为空。最常见的原因是停用词表把所有词都过滤掉了或者分词环节出错。先打印一下分词结果长度确认不是空列表。6.4 CSV文件读出来乱码现象用Excel打开Pandas保存的CSV中文全是乱码。原因Pandas默认保存CSV的编码是utf-8而Excel默认用GBK打开。解决方案df.to_csv(data.csv, encodingutf-8-sig)。用了utf-8-sig之后Excel就能正确识别了。6.5 性能瓶颈评论量大时的处理速度优化如果你的评论数据量达到了几万条用for循环逐条调用SnowNLP分析会非常慢。我实测下来1万条评论要跑20分钟以上。优化思路有两个多进程并行处理Python的multiprocessing库可以多进程并行跑情感分析。因为每条评论的分析是独立的天然适合并行。使用向量化操作如果只用情感词典法可以把所有评论合在一起分词统计用Pandas的groupby聚合速度能提升几十倍。但SnowNLP不支持向量化只能靠多进程解决。from multiprocessing import Pool def analyze_single(text): words tokenize(text) return final_sentiment(text, words) if __name__ __main__: with Pool(processes8) as pool: results pool.map(analyze_single, df[comment_text].tolist()) df[sentiment_score] results用过一次多进程之后我后来处理5万条评论大概只需要3分钟性价比非常高。7. 系统落地之后我得到的几条经验整个项目从零到能跑通我花了大半周的业余时间其中一半时间都耗在数据清洗和词典调整上。这里有几条很实在的经验分享给打算复现的朋友第一不要一上来就写情感分析代码。先把数据清洗做扎实。数据是垃圾模型再高级也白搭。我一开始偷懒跳过了清洗步骤直接对着原始数据分词结果词云图上全是景区门票排队这类词完全看不出情绪倾向。第二情感词典的迭代比你想象中重要。不要迷信通用情感词典要把自己数据里高频出现的口语词加进去特别是旅游场景特有的评价词。比如打卡这个词在旅游评论里频繁出现但它本身是中性的需要结合上下文判断。第三如果这是你的课程设计答辩时可以多讲几个为什么。为什么选SnowNLP而不是BERT为什么情感得分要和评分分开统计为什么要有自定义停用词表这些问题想清楚了应付提问绰绰有余。第四这个系统做好之后扩展空间非常大。比如接入更多平台的评论数据做横向对比或者把分析结果做成Web应用让用户输入景点名称就能看到分析报告。如果你想继续深入可以进一步尝试用LDA主题模型做评论主题聚类看看游客最关心的到底是交通、门票、风景还是服务这个价值更大。最后再说一个我个人的感触评论分析系统这类项目技术难度其实不高真正的门槛在数据理解和业务理解上。你会Python基础语法、会调几个库几天就能把代码写出来。但要把分析结果做得有说服力需要你反复检查每一类数据、每一个误判案例去理解用户真正想表达的意思。这个过程很磨人但也是最能学到东西的部分。希望这篇实战记录能让你少走一些弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价