1. 项目概述:用Flask构建智能酒店推荐系统
去年帮一家连锁酒店做数字化升级时,我遇到了一个典型场景:前台经常被客人问"有没有安静的大床房"或"能看到海景的双人间",而人工查询效率低下。这正是推荐系统的用武之地——通过Python Flask快速搭建一个能理解用户偏好的客房推荐引擎。这个系统不仅能根据房型、价格、景观等硬性条件筛选,还能基于用户历史行为做个性化排序。
Flask作为轻量级Web框架,特别适合此类中小型业务系统的快速原型开发。与Django相比,Flask的微内核设计让我们可以自由选择ORM、模板引擎等组件。在本项目中,我选用SQLAlchemy处理客房数据建模,用Jinja2渲染推荐结果页面,整个技术栈保持Python生态的一致性。系统核心功能包括:
- 多维度客房检索(价格区间、房型、设施等)
- 用户偏好学习(基于点击/预订行为)
- 推荐结果排序算法
- 管理员后台的数据看板
提示:虽然推荐算法可以很复杂,但初期建议先用简单规则(如加权评分)验证核心流程,后续再引入机器学习模型
2. 开发环境与工具链配置
2.1 Python环境搭建
推荐使用Python 3.8+版本,这是目前企业部署的主流选择。通过pyenv管理多版本Python是明智之举:
bash复制# 安装pyenv(Mac/Linux)
brew install pyenv
pyenv install 3.8.12
pyenv global 3.8.12
# Windows可用官方安装包
验证安装:
bash复制python --version # 应显示3.8.x
pip --version
2.2 Flask项目初始化
创建虚拟环境是避免依赖冲突的关键步骤:
bash复制python -m venv venv
source venv/bin/activate # Windows用venv\Scripts\activate
pip install flask sqlalchemy flask-sqlalchemy
项目基础结构建议如下:
code复制/hotel-recommender
├── app.py # 主入口
├── config.py # 配置参数
├── requirements.txt # 依赖清单
├── /static # 静态资源
├── /templates # Jinja2模板
└── /models # 数据模型
初始化Flask应用的最小代码:
python复制# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hotel Recommender Running!"
if __name__ == '__main__':
app.run(debug=True)
2.3 数据库选型与配置
对于客房数据这类结构化信息,关系型数据库是自然选择。SQLite适合开发阶段,生产环境可切换至PostgreSQL:
python复制# config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
初始化数据库扩展:
python复制# app.py
from flask_sqlalchemy import SQLAlchemy
from config import Config
app.config.from_object(Config)
db = SQLAlchemy(app)
3. 数据建模与客房信息管理
3.1 客房数据模型设计
核心实体包括客房、房型、设施等,以下是SQLAlchemy模型定义示例:
python复制# models/room.py
from datetime import datetime
from app import db
class RoomType(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True) # 如"豪华海景房"
description = db.Column(db.Text)
base_price = db.Column(db.Float)
max_occupancy = db.Column(db.Integer)
rooms = db.relationship('Room', backref='type', lazy='dynamic')
class Room(db.Model):
id = db.Column(db.Integer, primary_key=True)
room_number = db.Column(db.String(16), unique=True)
floor = db.Column(db.Integer)
view_type = db.Column(db.String(32)) # 海景/城景/园景
is_smoking = db.Column(db.Boolean, default=False)
status = db.Column(db.String(32)) # 可用/维修中/已预订
type_id = db.Column(db.Integer, db.ForeignKey('room_type.id'))
facilities = db.relationship('Facility', secondary='room_facility')
class Facility(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64)) # 如"迷你吧""浴缸"
icon = db.Column(db.String(128)) # 图标路径
room_facility = db.Table('room_facility',
db.Column('room_id', db.Integer, db.ForeignKey('room.id')),
db.Column('facility_id', db.Integer, db.ForeignKey('facility.id'))
)
3.2 管理员后台实现
使用Flask-Admin快速构建数据管理界面:
python复制from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
admin = Admin(app, name='酒店后台', template_mode='bootstrap3')
admin.add_view(ModelView(Room, db.session))
admin.add_view(ModelView(RoomType, db.session))
注意:生产环境务必添加身份验证,这里为演示简化
4. 推荐算法核心实现
4.1 基于规则的初级推荐
先实现基础筛选功能,处理明确的条件查询:
python复制# utils/recommender.py
from app.models import Room
def simple_recommend(filters):
query = Room.query.join(Room.type)
# 价格区间过滤
if 'min_price' in filters:
query = query.filter(RoomType.base_price >= filters['min_price'])
if 'max_price' in filters:
query = query.filter(RoomType.base_price <= filters['max_price'])
# 房型过滤
if 'room_type' in filters:
query = query.filter(RoomType.name == filters['room_type'])
# 景观类型
if 'view_type' in filters:
query = query.filter(Room.view_type == filters['view_type'])
return query.all()
4.2 个性化排序策略
记录用户行为(点击/收藏/预订)并计算偏好权重:
python复制# models/user.py
class UserAction(db.Model):
ACTION_TYPES = ['view', 'favorite', 'book']
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
room_id = db.Column(db.Integer, db.ForeignKey('room.id'))
action_type = db.Column(db.String(16))
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
# utils/recommender.py
def personalized_sort(rooms, user_id):
if not user_id:
return rooms
# 获取用户历史行为
actions = UserAction.query.filter_by(user_id=user_id).all()
# 计算房间得分(简化版)
room_scores = {}
for action in actions:
weight = 1 if action.action_type == 'view' else \
3 if action.action_type == 'favorite' else 5
room_scores[action.room_id] = room_scores.get(action.room_id, 0) + weight
# 带权排序
return sorted(rooms,
key=lambda x: room_scores.get(x.id, 0),
reverse=True)
4.3 混合推荐策略
结合内容过滤与协同过滤的思路:
python复制def hybrid_recommend(user_id, base_filters):
# 基础筛选
candidates = simple_recommend(base_filters)
# 个性化排序
sorted_rooms = personalized_sort(candidates, user_id)
# 多样性保证:前10个结果中同类型不超过3个
final_results = []
type_counter = {}
for room in sorted_rooms:
if len(final_results) >= 10:
break
room_type = room.type.name
if type_counter.get(room_type, 0) < 3:
final_results.append(room)
type_counter[room_type] = type_counter.get(room_type, 0) + 1
return final_results
5. 前端交互与API设计
5.1 RESTful API端点
python复制# routes/api.py
from flask import jsonify, request
from . import api_blueprint
@api_blueprint.route('/recommend', methods=['GET'])
def get_recommendations():
filters = {
'min_price': request.args.get('min_price', type=float),
'max_price': request.args.get('max_price', type=float),
'room_type': request.args.get('room_type'),
'view_type': request.args.get('view')
}
user_id = request.args.get('user_id') # 实际应从会话获取
recommendations = hybrid_recommend(user_id, filters)
return jsonify([r.to_dict() for r in recommendations])
5.2 推荐结果页面渲染
使用Jinja2模板展示推荐结果:
html复制<!-- templates/recommendations.html -->
{% extends "base.html" %}
{% block content %}
<div class="room-grid">
{% for room in recommendations %}
<div class="room-card">
<img src="{{ url_for('static', filename='images/' + room.type.name + '.jpg') }}">
<h3>{{ room.type.name }} #{{ room.room_number }}</h3>
<p>¥{{ "%.2f"|format(room.type.base_price) }} / 晚</p>
<ul class="facilities">
{% for fac in room.facilities %}
<li><i class="icon-{{ fac.icon }}"></i> {{ fac.name }}</li>
{% endfor %}
</ul>
<button class="book-btn" data-room-id="{{ room.id }}">立即预订</button>
</div>
{% endfor %}
</div>
{% endblock %}
6. 部署与性能优化
6.1 生产环境部署
使用Gunicorn+Nginx的经典组合:
bash复制pip install gunicorn
gunicorn -w 4 -b 127.0.0.1:8000 app:app
Nginx配置示例:
nginx复制server {
listen 80;
server_name hotel-recommender.example.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/your/static/files;
}
}
6.2 缓存策略优化
使用Redis缓存热门推荐结果:
python复制# extensions.py
from flask_redis import FlaskRedis
redis_client = FlaskRedis()
# utils/recommender.py
def get_cached_recommendations(user_id, filters):
cache_key = f"rec:{user_id}:{hash(frozenset(filters.items()))}"
cached = redis_client.get(cache_key)
if cached:
return pickle.loads(cached)
results = hybrid_recommend(user_id, filters)
redis_client.setex(cache_key, 3600, pickle.dumps(results)) # 缓存1小时
return results
7. 项目扩展方向
在实际运营中,我建议逐步添加以下功能:
- 实时竞价系统:让客房价格根据供需关系动态调整
- 用户画像系统:通过问卷收集初始偏好,完善冷启动问题
- AB测试框架:比较不同推荐算法的转化率
- 房态日历:可视化展示客房可用时段
- 多语言支持:适应国际旅客需求
一个容易忽略但重要的细节是推荐解释功能——告诉用户"为什么推荐这个房间"。这能显著提升信任度:
python复制def generate_explanation(room, user_id):
explanations = []
if user_id:
actions = UserAction.query.filter_by(
user_id=user_id,
room_id=room.id
).all()
if any(a.action_type == 'favorite' for a in actions):
explanations.append("您曾收藏过此类房间")
if room.view_type == 'sea_view':
explanations.append("海景房目前余量充足")
return "推荐理由:" + ";".join(explanations) if explanations else "优质推荐"
