1. 项目概述
这个旅游景点推荐系统是我最近完成的一个实战项目,核心思路是通过分析海量用户评论数据,挖掘出游客真实关注点和情感倾向,为景点推荐提供数据支撑。不同于传统基于评分或简单标签的推荐方式,我们深入文本内容,用NLP技术提取主题特征,再结合机器学习算法实现个性化推荐。
系统采用B/S架构,前端用Vue.js实现响应式界面,后端基于Django框架开发,数据存储在MySQL关系型数据库。整个系统分为用户端和管理端两大模块,完整实现了从数据采集、清洗分析到智能推荐的全流程。
提示:在实际开发中发现,旅游评论数据的质量直接影响推荐效果。我们特别加强了数据清洗环节,包括去重、去噪、分词等预处理步骤,确保后续分析的准确性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型与整体架构
系统采用典型的三层架构:
- 前端:Vue.js 2.x + Element UI
- 后端:Django 3.2 + Django REST framework
- 数据库:MySQL 5.7
选择这套技术栈主要基于以下考虑:
- Django自带强大的ORM和Admin后台,能快速构建数据管理功能
- Vue.js的组件化开发适合构建复杂的单页应用
- MySQL在中小规模数据场景下性能稳定,且与Python生态兼容性好
系统架构图如下(示意):
code复制[用户浏览器]
↑↓ HTTP/HTTPS
[Vue前端] ←→ [Django REST API]
↑↓
[MySQL数据库]
↑↓
[爬虫服务/数据分析服务]
2.2 核心模块划分
系统主要包含以下功能模块:
-
用户端功能:
- 景点浏览与搜索
- 评论发布与管理
- 个性化推荐
- 旅游资讯浏览
- 反馈与举报
-
管理端功能:
- 用户与权限管理
- 景点数据维护
- 评论审核
- 热度分析看板
- 系统监控
-
数据分析服务:
- 评论爬取与清洗
- LDA主题建模
- 情感分析
- 推荐算法引擎
3. 数据采集与处理
3.1 评论数据获取
我们开发了分布式爬虫系统,从多个主流旅游平台采集评论数据。关键技术点包括:
-
反爬策略应对:
- 动态User-Agent轮换
- IP代理池(自建)
- 请求频率控制(2-3秒/次)
- 验证码识别(使用Tesseract OCR)
-
数据字段设计:
python复制class Comment(models.Model):
content = models.TextField() # 评论内容
rating = models.FloatField() # 评分(1-5)
user_id = models.CharField(max_length=64) # 用户ID
spot_id = models.CharField(max_length=64) # 景点ID
platform = models.CharField(max_length=32) # 来源平台
publish_time = models.DateTimeField() # 发布时间
# 其他元数据字段...
3.2 数据清洗流程
原始评论数据需要经过严格清洗:
- 去重:基于用户ID+内容MD5值判重
- 去噪:
- 过滤广告(包含联系方式、链接等)
- 剔除无意义内容(如纯表情符号)
- 识别并删除机器生成内容
- 分词:使用jieba分词,加载旅游领域自定义词典
- 停用词过滤:扩展了中文常用停用词表
清洗后的数据存储结构示例:
json复制{
"comment_id": "C_10086",
"clean_text": "西湖 风景 优美 适合 拍照 但 人 太多",
"words": ["西湖", "风景", "优美", "适合", "拍照", "人", "太多"],
"tags": ["风景", "拥挤"]
}
4. 主题挖掘与情感分析
4.1 LDA主题模型实现
使用gensim库实现LDA主题挖掘:
python复制from gensim import corpora, models
# 构建词典和语料
dictionary = corpora.Dictionary(processed_docs)
corpus = [dictionary.doc2bow(doc) for doc in processed_docs]
# 训练LDA模型
lda_model = models.LdaModel(
corpus=corpus,
id2word=dictionary,
num_topics=10, # 根据困惑度评估确定
passes=15,
alpha='auto'
)
# 可视化主题
import pyLDAvis.gensim
pyLDAvis.enable_notebook()
vis = pyLDAvis.gensim.prepare(lda_model, corpus, dictionary)
vis
典型输出主题示例:
- Topic 1: 风景/拍照/美丽/自然 (35%)
- Topic 2: 交通/便利/位置/地铁 (22%)
- Topic 3: 门票/价格/性价比/收费 (18%)
- Topic 4: 服务/工作人员/态度/帮助 (15%)
- Topic 5: 拥挤/排队/人多/等待 (10%)
4.2 情感分析实现
结合情感词典和机器学习方法:
-
基于词典的方法:
- 使用BosonNLP情感词典
- 计算情感极性得分:
code复制情感得分 = (积极词数量 - 消极词数量) / 总词数
-
机器学习方法:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import LinearSVC
# 特征提取
vectorizer = TfidfVectorizer(max_features=5000)
X = vectorizer.fit_transform(comments)
# 训练情感分类器
clf = LinearSVC()
clf.fit(X_train, y_train) # y_train为人工标注的情感标签
# 预测新评论
def predict_sentiment(text):
vec = vectorizer.transform([text])
return clf.predict(vec)[0]
实际应用中,我们融合两种方法的结果,准确率达到89.7%(10折交叉验证)。
5. 推荐算法实现
5.1 随机森林回归算法
用于预测用户对景点的评分:
python复制from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
# 特征工程
features = ['topic1', 'topic2', ..., 'sentiment', 'user_avg_rating']
X = df[features]
y = df['actual_rating']
# 训练模型
rf = RandomForestRegressor(
n_estimators=200,
max_depth=10,
random_state=42
)
rf.fit(X_train, y_train)
# 评估
print("R^2 score:", rf.score(X_test, y_test))
关键参数调优过程:
- 使用GridSearchCV寻找最优参数组合
- 特征重要性分析:
code复制1. sentiment (0.32) 2. topic3_price (0.25) 3. user_avg_rating (0.18)
5.2 协同过滤改进
传统协同过滤的冷启动问题解决方案:
- 混合推荐策略:
python复制def hybrid_recommend(user_id, spot_id):
# 内容相似度
content_score = content_model.predict(user_id, spot_id)
# 协同过滤得分
if user_has_history(user_id):
cf_score = cf_model.predict(user_id, spot_id)
else:
cf_score = global_avg_rating
# 融合权重
return 0.6*content_score + 0.4*cf_score
- 新用户处理流程:
- 首次登录时填写兴趣问卷
- 结合地理位置推荐附近热门景点
- 随着交互增加逐步优化推荐
6. 系统实现细节
6.1 核心API设计
景点推荐接口示例:
python复制# views.py
class SpotRecommendView(APIView):
def get(self, request):
user_id = request.user.id
page = int(request.GET.get('page', 1))
# 获取推荐结果
recommender = HybridRecommender()
spots = recommender.recommend(user_id)
# 分页处理
paginator = Paginator(spots, 10)
page_spots = paginator.page(page)
# 序列化
serializer = SpotSerializer(page_spots, many=True)
return Response(serializer.data)
6.2 数据库优化
- 索引设计:
sql复制CREATE INDEX idx_comment_spot ON comment(spot_id);
CREATE INDEX idx_comment_user ON comment(user_id);
CREATE INDEX idx_rating ON comment(rating);
- 查询优化:
- 使用select_related/prefetch_related减少查询次数
- 热点数据Redis缓存
- 定期归档历史数据
6.3 前端关键实现
推荐结果可视化组件:
vue复制<template>
<div class="recommend-spots">
<h3>为您推荐的景点</h3>
<el-row :gutter="20">
<el-col
v-for="spot in spots"
:key="spot.id"
:xs="24" :sm="12" :md="8">
<spot-card :data="spot"/>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
spots: []
}
},
async mounted() {
const res = await this.$http.get('/api/recommend/')
this.spots = res.data.results
}
}
</script>
7. 部署与性能优化
7.1 生产环境部署
使用Docker-compose编排服务:
yaml复制version: '3'
services:
web:
build: .
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- redis
- db
db:
image: mysql:5.7
environment:
MYSQL_DATABASE: 'travel'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'root'
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
db_data:
7.2 性能优化措施
-
缓存策略:
- 使用Redis缓存热门景点数据
- 实现HTTP缓存头(ETag、Last-Modified)
- 前端静态资源CDN加速
-
异步任务:
python复制# tasks.py
@shared_task
def update_recommendations(user_id):
# 耗时推荐计算
spots = Recommender().compute_for_user(user_id)
cache.set(f'rec_{user_id}', spots, timeout=3600)
- 数据库读写分离:
- 主库负责写操作
- 从库处理读请求
- 使用Django-routers实现自动路由
8. 实际应用效果
8.1 推荐质量评估
使用A/B测试对比传统方法和我们的系统:
| 指标 | 传统方法 | 本系统 | 提升 |
|---|---|---|---|
| CTR (点击率) | 12.3% | 18.7% | +52% |
| 平均停留时长 | 2.1min | 3.4min | +62% |
| 用户满意度评分 | 3.8/5 | 4.5/5 | +18% |
8.2 典型用户场景
-
旅游规划阶段:
- 系统根据用户历史偏好推荐主题相关景点
- 显示"喜欢A景点的用户也喜欢B"的关联推荐
- 提供基于情感分析的真实评价摘要
-
景区管理视角:
- 实时监控游客反馈热点
- 识别服务短板(如排队时间长)
- 评估营销活动效果
9. 遇到的问题与解决方案
9.1 数据稀疏性问题
问题表现:
- 新景点缺乏足够评论数据
- 部分用户交互行为稀少
解决方案:
- 引入迁移学习,复用其他领域训练好的模型
- 设计混合推荐策略,结合内容特征和协同过滤
- 使用基于图的推荐算法挖掘潜在关联
9.2 实时性挑战
问题表现:
- 新评论产生后推荐更新延迟
- 热门景点变化响应慢
优化方案:
- 实现增量式LDA训练
- 设计两级缓存策略:
- 短期缓存(5分钟):Redis存储
- 长期缓存(1小时):数据库存储
- 使用Kafka处理实时数据流
10. 项目扩展方向
-
多模态分析:
- 融合图片评论分析(使用CNN提取视觉特征)
- 语音评论情感识别
-
时空推荐:
- 结合用户实时位置
- 考虑季节/天气因素
-
个性化行程规划:
- 基于推荐结果自动生成游玩路线
- 整合交通、餐饮等周边服务
这个项目从构思到上线历时6个月,最大的体会是:旅游推荐不是简单的算法问题,需要深入理解用户决策心理。我们团队通过大量用户访谈发现,游客最看重的是真实性和个性化,这也是我们在设计时特别强调评论分析和主题挖掘的原因。
