news 2026/10/3 9:32:39

民宿评论数据分析:从爬虫采集到情感分析的完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
民宿评论数据分析:从爬虫采集到情感分析的完整实践

简介:一款基于Python开发的民宿用户生成内容(UGC)挖掘与分析软件,面向旅游大数据分析人员、爬虫与NLP学习者,专注解决美团、携程平台民宿评论的采集与深度分析难题。项目实现自动化评论采集、深度清洗、智能主题提取和细粒度情感分析,并附带图形界面,便于实时查看分析结果。资源共29个文件,以Python脚本为核心,包含携程与真果爬虫、GUI主程序、数据分析绘图脚本,辅以png/jpeg可视化图表、xml工程配置、txt说明文档及README,整体压缩包约1.86MB。目前已有79人学习浏览,适合希望了解真实平台数据采集、评论文本挖掘或情感分析完整流程的开发者参考。通过该项目可掌握爬虫异常处理、评论数据清洗、主题建模及情感极性判别等实用技能,并可直接运行GUI观察民宿评论多维度分析效果。

1. 民宿评论分析,卡在评分和文本对不上

美团和携程的民宿评论区有个常见的怪现象:用户打 4 分,评论里却在骂房东态度差;用户打 2 分,写的内容却全是夸房间干净。评分和文本描述经常对不上,导致只看分数根本没法判断一家民宿的真实口碑。这个项目就是围绕这个问题做的——它把美团和携程的民宿评论抓下来,做深度清洗,再用主题提取和细粒度情感分析把“用户到底在夸什么、骂什么”拆开。它解决的并不是爬虫本身,而是爬下来之后怎么让文本变成可靠的分析结果。

适合谁用?做民宿运营的人需要知道差评集中在哪个房间、哪个环节;做竞品分析的人需要批量看同城对手的评论结构;做毕业设计或数据比赛的人需要一套完整的采集加分析流程参考。这套资源从 requests 采集到 pandas 清洗,再到 LDA 主题提取和情感分析都有对应的 Python 代码,拿来改一改就能接到自己的数据源上。

2. 采集层设计:美团和携程的反爬策略完全不同

2.1 评论列表的抓取链路

这个项目的采集模块并不是一个通用的爬虫,而是针对美团和携程两家平台分别写的采集器。原因很简单——两个平台的页面渲染方式和反爬策略完全不一样。携程的民宿评论数据直接嵌在 HTML 里,结构相对规整,用 requests 加 BeautifulSoup 就能解析;美团则大量走接口返回 JSON,很多关键字段是异步加载出来的,直接抓 HTML 什么都拿不到。

我一般会先抓列表页,再根据列表页里的评论链接去抓详情页;但这个项目的做法更直接——抓评论列表本身就是抓目标,不需要进入详情页。采集器构造了一个带基础 Cookie 的 session,然后按城市和关键词去翻页拉评论列表。翻页方式上美团走的是 offset 分页参数,携程则是 page 参数,两者都需要在请求头里带上来源 referer,否则服务器直接拒绝。

import requests from bs4 import BeautifulSoup session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept-Language": "zh-CN,zh;q=0.9", }) def fetch_meituan_comments(city_id, keyword, offset=0): url = "https://www.meituan.com/api/comments" params = {"cityId": city_id, "keyword": keyword, "offset": offset} resp = session.get(url, params=params) data = resp.json() return data.get("comments", []) def fetch_ctrip_comments(city_id, keyword, page=1): url = "https://hotels.ctrip.com/comments" params = {"cityId": city_id, "keyword": keyword, "page": page} resp = session.get(url, params=params) soup = BeautifulSoup(resp.text, "html.parser") items = soup.select(".commentItem") return [item.get_text(strip=True) for item in items]

这里有几个参数需要根据实际情况调整。city_id在城市列表页里能直接查到,美团和携程的编码规则不同,建议先手动打开网页确认;offset美团接口一般以 0 起步,每次加 10;page携程从 1 开始,每次加 1。评论字段里最有价值的是用户名、评分、评论时间、评论内容、房型、回复内容这几列,清洗后基本能覆盖后续所有分析。

2.2 增量采集与入库容错

上一版我直接跑全量采集,结果跑了两天发现数据库里一半是重复的。这个项目在入库前做了两层去重:第一层用 MD5 对评论内容的指纹做唯一索引,第二层用「用户 ID + 评论时间」做逻辑判断,因为同一个用户在同一时间对同一家民宿只能发一条评论。平台对采集频率非常敏感,我习惯在每次请求之间加 2 到 3 秒的随机延时,美团和携程都要加,只是延时区间略有不同,美团 2 到 4 秒,携程 1.5 到 3 秒。

import time import random from hashlib import md5 def dedup_and_save(comment, collection): fingerprint = md5(comment["content"].encode("utf-8")).hexdigest() if collection.find_one({"fingerprint": fingerprint}): return False comment["fingerprint"] = fingerprint collection.insert_one(comment) return True # 抓完一页后进入入库流程 for c in page_comments: ok = dedup_and_save(c, db.reviews) if ok: time.sleep(random.uniform(2, 4))

爬虫的健壮性很大程度体现在异常处理上。网络请求超时、返回非 JSON、IP 被临时限流,这三类异常必须分别处理。超时重试三次还失败就跳过,非 JSON 说明返回的是验证页,这时候要立刻停下来检查 Cookie 是否过期。限流通常会伴随 HTTP 429 状态码,遇到后停 60 秒再继续。

3. 深度清洗:民宿评论比酒店评论脏得多

3.1 文本噪声的常见来源

民宿评论和酒店评论最大的区别是:民宿用户写评语更随意,什么玩意儿都往里面塞。表情符号、房间照片里的文字、房东回复里嵌套的客套话、各种平台的推广口令,全都会混进评论文本里。如果不做清洗,后面做主题提取和情感分析时,这些噪声会直接污染词频统计结果。

常见的噪声类型大致有这几类:第一种是表情符号和特殊字符,比如“房间很干净👍房东超好”,这个大拇指必须去掉;第二种是 URL 和微信号之类的内容,很多民宿房东会在评论回复里留联系方式;第三种是复制粘贴的户型描述,很多用户直接复制平台上的房间介绍作为评论;第四种是繁体字和异体字,携程平台上有不少港澳台用户写的评论。这些在分词之前必须处理干净。

import re import pandas as pd def clean_comment(text): # 去掉表情符号 text = re.sub(r"[\U0001F300-\U0001FAFF]", "", text) # 去掉URL text = re.sub(r"http[s]?://\S+", "", text) # 去掉微信号和手机号 text = re.sub(r"[vV][xX][::]\s*[a-zA-Z0-9_-]+", "", text) text = re.sub(r"1[3-9]\d{9}", "", text) # 全角转半角 text = text.replace(",", ",").replace("。", ".").replace("!", "!").replace("?", "?") # 繁体转简体,需要引入zhconv from zhconv import convert text = convert(text, "zh-cn") return text.strip() df["cleaned"] = df["comment"].apply(clean_comment)

zhconv库负责繁体转简体,转换前要先做全角转半角,不然转换结果会混入异常字符。表情符号的正则范围用了\U0001F300-\U0001FAFF,这个区间覆盖了绝大部分 Emoji,但有些特殊符号比如 ™ 和 © 不在范围内,需要另外写规则。

3.2 重复评论去重与空值处理

民宿评论有个很头疼的现象——同一家民宿的多个账号发的评论内容几乎一模一样,这种是刷单产生的,不做去重会直接扭曲情感分布。普通的 MD5 去重只对完全相同的文本有效,稍微改几个字的重复评论就绕过了。所以我一般会用 SimHash 加海明距离的思路,相似度超过阈值就视为重复。

from simhash import Simhash def is_dup(new_text, existing_hashes, threshold=3): new_hash = Simhash(new_text) for h in existing_hashes: if new_hash.distance(h) < threshold: return True return False

清洗完之后的字段还要检查完整度:评分缺失的评论直接丢,内容为空的丢,只有标点符号的丢。空评论和纯表情评论在民宿场景里很常见,很多人习惯只打分不写字,这种评论对文本分析没有任何贡献。处理完后会明显看到数据量缩水,而且文本质量高了一大截。

4. 主题提取与情感分析:短文本里挖出真实口碑

4.1 基于词典的主题匹配

民宿评论文本的特殊性在于句子短、口语化严重、主题高度集中。直接上 LDA 往往效果很差,因为一行十几个词的文本没有足够的共现信息支撑主题模型。这个项目采用的方式是构建民宿领域词典,做关键词匹配,把评论归类到「房间」「位置」「卫生」「服务」「餐食」「性价比」这几个主题下。

词典的质量直接决定主题匹配的效果。项目里已经内置了一版约 120 个词的民宿词典,覆盖了房间设施、周边交通、房东服务等维度。实际使用时需要根据本地数据增补,比如“落地窗”“投影仪”“榻榻米”这类词在特定城市的民宿评论里出现频率很高。匹配用最简单的方式——遍历词典,只要评论里出现该主题下的词,就把这条评论计入该主题。

topic_dict = { "房间": ["干净", "床品", "空调", "热水", "隔音", "窗户", "投影", "榻榻米"], "位置": ["地铁", "商圈", "步行", "交通", "方便", "找不到", "导航"], "服务": ["房东", "热情", "回复", "入住", "退房", "接送", "沟通"], "餐食": ["早餐", "厨房", "餐具", "冰箱", "做饭", "味道"], "卫生": ["蟑螂", "灰尘", "异味", "发霉", "整洁", "床单"], } def assign_topic(text): matched = [] for topic, words in topic_dict.items(): for w in words: if w in text: matched.append(topic) break return matched or ["未分类"]

这里有一个需要注意的地方:同一主题下的词要避免互斥,比如“干净”既可以归房间也可以归卫生,误分类会带来统计偏差。我一般会把这类词只保留在主导主题下,或者允许一条评论归属多个主题,统计时做多重计数。这是两种不同的统计口径,看场景选,项目里默认是多重计数。

4.2 细粒度情感的本地化修正

情感分析用的是 SnowNLP 作为基础模型,但拿到民宿场景里直接用效果并不好。通用模型会在两个地方翻车:一是“房东人很好”这种包含明确正面评价的句子被判成中性;二是“绝绝子”“YYDS”这种网络表达完全不在模型语料里。项目在基础模型之上做了一层规则修正,把民宿场景里的高频情感词做成正负向词典,对模型结果做二次覆盖。

from snownlp import SnowNLP positive_words = ["很棒", "满意", "推荐", "贴心", "舒适", "惊喜", "赞"] negative_words = ["垃圾", "失望", "脏", "差", "坑", "后悔", "投诉", "气愤"] def sentiment_correct(text, base_score): pos_count = sum(1 for w in positive_words if w in text) neg_count = sum(1 for w in negative_words if w in text) if pos_count > neg_count and base_score < 0.5: return base_score + 0.3 if neg_count > pos_count and base_score > 0.5: return base_score - 0.3 return base_score def analyze_sentiment(text): score = SnowNLP(text).sentiments return sentiment_correct(text, score)

情感得分的阈值设定需要根据数据分布调整。默认以 0.5 为中立分界,但民宿评论整体偏正向,很多表达一般的评论得分也在 0.6 以上。实际操作中我会先把所有评论的得分跑一遍看直方图,找到明显的分界谷值,再设定正负向阈值,而不是硬套一个 0.5。

5. 避坑记录:民宿评论分析最容易翻车的五个地方

5.1 携程评论时间戳经常出现缺失值

现象:采集到的携程评论里,有一部分评论的发布时间是空字符串。
原因:携程出于业务考虑,部分老评论不展示具体日期,只显示“入住后评价”之类的文案。
解决:不能直接丢弃这些评论,它们是有效内容。我一般会把时间字段标记为“未知”,在按时间维度做趋势分析时单独排除,但在主题和情感统计时仍然计入。

5.2 美团评论接口返回的评分值是乘以 2 的

现象:美团评论接口返回的评分字段最大值是 10,而不是 5。
原因:美团内部存储评分时用的是十分制,前端展示时做了除以 2 的处理,但接口层没有处理。
解决:在清洗阶段统一除以 2,转成五分制。否则后续不管做均值统计还是分箱分析,数值都会整体偏高一倍。

5.3 民宿名称和实际位置经常对不上

现象:美团上很多民宿的名称是“XX度假别墅”“XX花园小院”,但实际坐标和名称描述的位置相差几公里。
原因:民宿主在平台注册时填写的地址比较随意,加上平台对民宿位置的审核没有酒店严格。
解决:分析位置相关主题时,不能依赖名称,必须用评论里提到的地标词来判断实际区位,比如“离地铁站 500 米”“步行到西湖”。

5.4 情感分析对小样本分类极度不稳定

现象:同一条评论跑两遍情感分析,结果判定的强度不一样。
原因:SnowNLP 内部用了贝叶斯分类,训练语料对民宿领域覆盖不足,导致边界样本的判定方差很大。
解决:对情感得分在 0.4 到 0.6 之间的评论,强制归入中立区间,不做正负判定。这样虽然损失了一部分细粒度,但整体稳定性大幅提升。

5.5 主题词典不覆盖网络新词

现象:大量评论被分到“未分类”,原因是里面出现了“打卡”“出片”“氛围感”这类新词。
原因:静态词典对网络流行语的覆盖有滞后性。
解决:每个月用 TF-IDF 跑一次高频词增量提取,人工审核后将新的高频词补充进词典。这个操作五分钟就能做完,但往往能救回 10% 以上的未分类样本。

6. 结果验证与可视化:让分析结果能被信任

民宿评论分析如果只停留在“跑出几个饼图”的阶段,很难被信服。我习惯在交付结果前做一次完整的数据质量验证。最基础的一条是采样人工标注对比,从全量数据里随机抽 200 条评论,人工标注正负向,再和模型判定结果做对比,准确率到 85% 以上才算合格。

主题分布做交叉验证时要特别注意:把同一批评论按月份拆分,分别跑主题分布,如果两个月的分布差异超过 15%,大概率是采集样本出了偏差,而不是市场发生了变化。情感分析同理,要按城市、按房型、按价格段三个维度分别统计,才能发现真正有价值的结论。

可视化层是这个项目比较出彩的地方——它内置了一套简单的交互式报表页面,用 Flask 提供数据接口,前端用 ECharts 渲染。采集和分析完成后,可以直接在浏览器里按城市、月份、主题筛选评论数据,看到不同主题的情感得分变化趋势。

python app.py --port 8080 --data ./data/reviews_clean.csv

启动后访问http://localhost:8080/dashboard就能看到完整的分析面板。这个面板对于民宿运营者来说非常实用,能直观看出某个区域的民宿的主要差评集中在“卫生”还是“隔音”,从而针对性改善。

从那以后我每次跑民宿数据,都强制先做一轮采样人工标注,再决定要不要信模型的结果。这个习惯帮我避掉不少“模型说用户很满意、实际被投诉到平台”的尴尬情况。希望这份资源能帮你在民宿评论分析这条路上少走几个来回。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 9:32:39

MySQL驱动避坑指南:从ODBC位数到SSL认证的完整排错思路

把连接MySQL时跳出来的那些稀奇古怪的报错翻了一遍之后&#xff0c;我越来越觉得“MySQL驱动”可能是数据库圈子里最被低估的拦路虎。前阵子帮一个同事处理Excel导数据的问题&#xff0c;他电脑是Windows 11 64位&#xff0c;服务器跑的是MySQL 8.0&#xff0c;结果在Excel里选…

作者头像 李华
网站建设 2026/10/3 9:31:02

OpenSim符号肌肉力矩臂计算:告别数值差分,获得解析解

简介&#xff1a;这套源码用于实现基于OpenSim的符号肌肉力矩臂计算&#xff0c;面向生物力学研究人员与运动仿真方向学习者&#xff0c;解决肌肉与关节之间力学关系的量化分析与可视化问题。压缩包约2.97MB&#xff0c;共16个文件&#xff0c;包含Python脚本、C头文件与源文件…

作者头像 李华
网站建设 2026/10/3 9:30:54

爬虫+知识图谱+模板问答:军事武器KG问答系统实战

简介&#xff1a;这是一套面向军事装备数据采集与智能问答的完整实战项目&#xff0c;适合正在学习Scrapy爬虫、知识图谱构建及自然语言查询的开发者参考。项目围绕“武器装备知识图谱”展开&#xff0c;覆盖爬虫抓取、数据清洗、MongoDB存储、图谱建模与问答推理等关键环节&am…

作者头像 李华
网站建设 2026/10/3 9:30:54

MySQL InnoDB存储引擎核心原理与性能优化实践指南

在MySQL的世界里&#xff0c;存储引擎就是那个决定数据怎么存、怎么读、怎么并发、怎么崩溃恢复的底层执行者。很多同学聊起InnoDB&#xff0c;第一反应就是“它支持事务、支持行锁”&#xff0c;然后面试问深一点就卡住了。问为什么要用B树而不是B树、为什么RR隔离级别能防幻读…

作者头像 李华
网站建设 2026/10/3 9:29:51

MATLAB中WVD信号分析避坑指南:高精度时频显微镜实战

1. 为什么WVD不是“另一个时频图”&#xff0c;而是信号分析里的“高精度显微镜” 最近帮三个做振动故障诊断的工程师朋友调试轴承早期微弱冲击信号&#xff0c;他们一开始都用STFT&#xff08;短时傅里叶变换&#xff09;——图看着规整、代码好写、MATLAB里一行 spectrogram…

作者头像 李华
网站建设 2026/10/3 9:28:21

PostgreSQL锁竞争排查:pg_blocking_pids定位阻塞者实战

1. 锁竞争排查的核心思路1.1 数据库“卡住”了&#xff0c;从哪下手&#xff1f;做 PostgreSQL 运维或者开发的同学&#xff0c;肯定都遇到过这种情况&#xff1a;一条简单的 UPDATE 或者 SELECT 突然就跑不动了&#xff0c;应用侧一直转圈&#xff0c;监控面板上的活跃会话数直…

作者头像 李华