1. 项目概述:基于Flask的重庆旅游推荐系统
去年夏天,我接到了一个来自重庆本地旅行社的需求——他们希望开发一个能够智能推荐旅游路线的系统。经过两个月的开发和迭代,我们最终完成了一个基于Python Flask框架的重庆旅游推荐系统。这个系统不仅能够实时抓取各大平台的旅游数据,还能通过算法为游客提供个性化的推荐方案。
这个系统的核心价值在于解决了游客在规划行程时的三大痛点:信息过载、选择困难和决策效率低。通过爬虫技术,我们整合了携程、美团等平台的景点、美食、住宿数据;利用Flask构建了轻量但高效的后端服务;最后通过数据可视化,让复杂的旅游信息一目了然。
系统主要面向三类用户:
- 自由行游客:可以根据个人偏好获取定制化推荐
- 旅行社从业者:快速了解各景点实时热度
- 本地商家:掌握自身在平台上的数据表现
技术选型上,我们选择了Python技术栈,主要考虑因素包括:
- 爬虫生态成熟(Scrapy/Requests)
- 数据处理能力强(Pandas/Numpy)
- 快速开发(Flask轻量灵活)
- 可视化支持完善(ECharts/Pyecharts)
2. 系统架构设计
2.1 整体技术架构
系统采用典型的三层架构设计:
code复制前端展示层(HTML+CSS+JS)
↑↓
业务逻辑层(Flask)
↑↓
数据访问层(MySQL/MongoDB)
↑↓
数据采集层(爬虫系统)
这种分层设计带来了三个显著优势:
- 模块解耦:各层可以独立开发和部署
- 扩展性强:例如可以单独升级爬虫模块而不影响其他部分
- 维护方便:问题定位和修复更加高效
2.2 核心模块划分
系统包含6个核心功能模块:
- 数据采集模块:负责从各平台抓取原始数据
- 数据处理模块:清洗、去重、结构化原始数据
- 存储模块:管理数据库读写操作
- 推荐算法模块:实现个性化推荐逻辑
- 可视化模块:生成各类数据图表
- 用户交互模块:处理前端请求和响应
3. 爬虫系统实现细节
3.1 爬虫技术选型
我们对比了两种主流爬虫方案:
| 方案 | Requests+BeautifulSoup | Scrapy框架 |
|---|---|---|
| 优点 | 学习成本低,适合简单页面 | 功能完善,自带去重、并发等机制 |
| 缺点 | 需要自行处理并发和去重 | 学习曲线较陡峭 |
| 适用场景 | 数据量小、页面结构简单 | 大规模、复杂的爬取任务 |
最终我们采用混合方案:
- 对携程等反爬严格的平台使用Scrapy
- 对结构简单的本地生活网站使用Requests
3.2 关键爬虫代码示例
python复制import scrapy
from bs4 import BeautifulSoup
class CtripSpider(scrapy.Spider):
name = 'ctrip'
start_urls = ['https://you.ctrip.com/sight/chongqing158.html']
def parse(self, response):
soup = BeautifulSoup(response.text, 'lxml')
items = soup.select('.list_mod2 .rdetailbox')
for item in items:
yield {
'name': item.select_one('.rdetailbox_title a').text.strip(),
'score': float(item.select_one('.score .cur').text),
'reviews': int(item.select_one('.recomment').text.replace('条点评','')),
'address': item.select_one('.icon_location+span').text.strip()
}
3.3 反爬应对策略
在开发过程中,我们遇到了几个典型的反爬问题:
- IP封禁:解决方案是使用代理IP池,配合随机延迟
- 验证码:引入第三方打码平台服务
- 动态加载:使用Selenium模拟浏览器行为
- 数据混淆:开发特定的解析规则处理乱码数据
重要提示:爬虫开发必须遵守robots.txt协议,控制请求频率,避免对目标网站造成过大压力。
4. 数据处理与存储
4.1 数据清洗流程
原始爬取数据通常包含大量噪声,我们的清洗流程包括:
- 去重:基于URL和内容特征值双重去重
- 补全:自动填充缺失的必要字段
- 纠错:修正明显的格式错误(如价格单位混淆)
- 标准化:统一不同来源的数据格式
python复制import pandas as pd
def clean_data(df):
# 去除重复项
df = df.drop_duplicates(subset=['name','address'])
# 处理缺失值
df['price'] = df['price'].fillna(df.groupby('category')['price'].transform('median'))
# 格式标准化
df['score'] = pd.to_numeric(df['score'], errors='coerce')
df['open_time'] = df['open_time'].apply(standardize_time)
return df
4.2 数据库设计
系统使用MySQL作为主数据库,主要表结构设计如下:
景点表(spots)
sql复制CREATE TABLE `spots` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`category` varchar(50) NOT NULL,
`score` decimal(3,1) DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
`longitude` decimal(10,6) DEFAULT NULL,
`latitude` decimal(10,6) DEFAULT NULL,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_category` (`category`),
KEY `idx_location` (`longitude`,`latitude`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
用户行为表(user_actions)
sql复制CREATE TABLE `user_actions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`spot_id` int(11) NOT NULL,
`action_type` enum('view','collect','share') NOT NULL,
`action_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`),
KEY `idx_spot` (`spot_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5. 推荐算法实现
5.1 推荐系统架构
我们的推荐系统采用混合推荐策略:
code复制用户显式偏好(筛选条件)
↓
基于内容的推荐 → 混合推荐结果
↑
协同过滤推荐(用户行为数据)
5.2 核心算法代码
python复制from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import TfidfVectorizer
class Recommender:
def __init__(self, spots_df):
self.spots = spots_df
self.vectorizer = TfidfVectorizer()
# 构建内容特征矩阵
features = spots_df['name'] + ' ' + spots_df['category'] + ' ' + spots_df['tags']
self.feature_matrix = self.vectorizer.fit_transform(features)
def content_based_recommend(self, spot_id, top_n=5):
# 计算内容相似度
idx = self.spots[self.spots['id']==spot_id].index[0]
sim_scores = cosine_similarity(self.feature_matrix[idx], self.feature_matrix)
# 获取最相似的景点
similar_indices = sim_scores.argsort()[0][-top_n-1:-1][::-1]
return self.spots.iloc[similar_indices]
def hybrid_recommend(self, user_preferences, user_history=None, top_n=10):
# 实现混合推荐逻辑
...
5.3 推荐策略优化
在实际运行中,我们通过AB测试不断优化推荐策略:
- 冷启动问题:新用户采用热门推荐+地域推荐策略
- 多样性问题:在推荐结果中混入20%的探索性内容
- 实时性:用户最新行为会立即影响后续推荐
- 季节因素:根据月份自动调整户外景点的权重
6. 数据可视化实现
6.1 可视化技术选型
对比了三种主流方案:
| 方案 | Matplotlib | Plotly | Pyecharts |
|---|---|---|---|
| 交互性 | 弱 | 强 | 强 |
| 学习曲线 | 平缓 | 中等 | 中等 |
| 美观度 | 一般 | 优秀 | 优秀 |
| 与Flask集成 | 需转图片 | 直接支持 | 直接支持 |
最终选择Pyecharts,主要因为:
- 丰富的图表类型
- 良好的中文支持
- 与Flask无缝集成
- 支持响应式设计
6.2 典型可视化案例
景点热度热力图
python复制from pyecharts.charts import Geo
from pyecharts import options as opts
def create_heatmap(data):
geo = (
Geo()
.add_schema(maptype="重庆")
.add(
"热度",
data,
type_="heatmap",
label_opts=opts.LabelOpts(is_show=False),
)
.set_global_opts(
visualmap_opts=opts.VisualMapOpts(),
title_opts=opts.TitleOpts(title="重庆景点热度分布"),
)
)
return geo.render_embed()
价格分布箱线图
python复制from pyecharts.charts import Boxplot
def price_boxplot(data):
boxplot = (
Boxplot()
.add_xaxis(["景点门票", "酒店价格", "餐饮消费"])
.add_yaxis("价格分布", data)
.set_global_opts(
title_opts=opts.TitleOpts(title="重庆旅游价格分布"),
yaxis_opts=opts.AxisOpts(name="价格(元)"),
)
)
return boxplot.render_embed()
7. 系统部署与性能优化
7.1 生产环境部署
我们采用Nginx + Gunicorn + Flask的部署方案:
code复制客户端 ←→ Nginx(反向代理/静态文件) ←→ Gunicorn(WSGI服务器) ←→ Flask应用
关键部署步骤:
- 使用Gunicorn启动Flask应用:
bash复制gunicorn -w 4 -b 127.0.0.1:8000 app:app
- Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static {
alias /path/to/static/files;
expires 30d;
}
}
7.2 性能优化措施
-
数据库优化:
- 添加合适的索引
- 使用连接池
- 读写分离(高峰期)
-
缓存策略:
- Redis缓存热门推荐结果
- 静态资源CDN加速
- 浏览器端缓存控制
-
异步处理:
- 使用Celery处理耗时任务(如数据更新)
- 邮件发送等非核心功能异步化
-
监控告警:
- Prometheus监控关键指标
- 异常自动告警(邮件/短信)
8. 项目总结与经验分享
在开发这个系统的过程中,我们积累了几个重要的经验:
-
爬虫稳定性:一定要设计完善的重试机制和监控系统,我们曾经因为目标网站改版导致数据中断了一天而没及时发现。
-
数据质量:建立数据质量监控指标(如完整性、时效性),比我们最初想象的要重要得多。
-
推荐算法:不要一开始就追求复杂的算法,我们从简单的规则引擎开始,逐步迭代的效果反而更好。
-
性能瓶颈:在压力测试下,最先出现问题的往往是数据库查询,而不是Python代码本身。
对于想要开发类似系统的开发者,我的建议是:
- 先从一个小而完整的功能闭环开始
- 尽早建立自动化测试和部署流程
- 监控系统要同步建设,不能事后补
- 保持代码良好的可扩展性,旅游数据维度很可能会不断增加
这个系统目前已经在重庆本地三家旅行社投入使用,平均为用户节省了40%的行程规划时间。未来我们计划加入实时人流预测、个性化路线生成等更智能的功能。