用Python构建个人电影推荐系统的全流程实战最近几年电影推荐系统变得越来越受欢迎。作为一个电影爱好者你是否想过自己动手搭建一个个性化的电影推荐平台本文将带你从零开始使用Python和豆瓣API构建一个完整的电影推荐系统。不同于简单的API调用教程我们会涵盖数据获取、处理、存储到可视化展示的全流程最终打造一个可以实际使用的个人电影看板。1. 准备工作与环境搭建在开始之前我们需要准备好开发环境。推荐使用Python 3.8或更高版本并安装以下必要的库pip install requests pandas flask flask-sqlalchemyrequests用于发送HTTP请求获取API数据pandas数据处理和分析flask构建简单的Web应用flask-sqlalchemy数据库操作提示建议使用虚拟环境来管理项目依赖避免与其他项目产生冲突。1.1 获取豆瓣API访问权限豆瓣提供了开放的API接口但需要申请API Key才能使用。目前豆瓣对新申请的API Key有较严格的审核建议访问豆瓣开发者平台注册账号仔细阅读API使用条款申请API Key时详细说明使用目的如果暂时无法获取个人API Key可以使用公开的测试Key但可能有调用频率限制。2. 豆瓣API数据获取与解析2.1 基础API调用让我们从最基本的API调用开始。豆瓣电影API提供了多种数据接口包括正在热映、Top250、即将上映等。下面是一个获取正在热映电影的示例import requests def get_in_theaters_movies(city北京, start0, count10): url https://api.douban.com/v2/movie/in_theaters params { apikey: 你的API_KEY, city: city, start: start, count: count } response requests.get(url, paramsparams) if response.status_code 200: return response.json() else: print(f请求失败状态码{response.status_code}) return None2.2 数据结构解析豆瓣API返回的是JSON格式的数据我们需要了解其结构才能有效提取信息。以热映电影接口为例返回数据主要包含以下字段字段名类型描述countint当前返回的电影数量startint起始位置totalint总电影数量subjectslist电影列表每个电影对象又包含丰富的信息{ rating: { max: 10, average: 8.5, min: 0 }, genres: [剧情, 犯罪], title: 电影名, casts: [{name: 演员1}, {name: 演员2}], directors: [{name: 导演}], year: 2023, images: {large: 海报URL}, id: 电影ID }2.3 多接口数据获取除了热映电影我们还可以获取其他类型的电影数据def get_top250(start0, count10): url https://api.douban.com/v2/movie/top250 params { apikey: 你的API_KEY, start: start, count: count } return requests.get(url, paramsparams).json() def get_coming_soon(start0, count10): url https://api.douban.com/v2/movie/coming_soon params { apikey: 你的API_KEY, start: start, count: count } return requests.get(url, paramsparams).json()3. 数据存储与管理获取到数据后我们需要将其存储起来以便后续使用。这里我们使用SQLite数据库它轻量且无需额外配置。3.1 数据库设计设计一个简单的电影数据库表结构from flask_sqlalchemy import SQLAlchemy from flask import Flask app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] sqlite:///movies.db app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False db SQLAlchemy(app) class Movie(db.Model): id db.Column(db.String(20), primary_keyTrue) title db.Column(db.String(100), nullableFalse) original_title db.Column(db.String(100)) year db.Column(db.String(10)) genres db.Column(db.String(200)) rating db.Column(db.Float) directors db.Column(db.String(200)) casts db.Column(db.String(500)) image_url db.Column(db.String(200)) summary db.Column(db.Text) type db.Column(db.String(20)) # in_theaters/top250/coming_soon def __repr__(self): return fMovie {self.title}3.2 数据存储函数编写将API数据存储到数据库的函数def save_movies_to_db(movies_data, movie_type): for movie in movies_data[subjects]: existing Movie.query.get(movie[id]) if existing: continue new_movie Movie( idmovie[id], titlemovie[title], original_titlemovie.get(original_title, ), yearmovie.get(year, ), genres,.join(movie.get(genres, [])), ratingmovie.get(rating, {}).get(average, 0), directors,.join([d[name] for d in movie.get(directors, [])]), casts,.join([c[name] for c in movie.get(casts, [])]), image_urlmovie.get(images, {}).get(large, ), typemovie_type ) db.session.add(new_movie) db.session.commit()4. 构建电影推荐Web应用有了数据基础现在我们可以构建一个简单的Web应用来展示和推荐电影。4.1 Flask基础框架创建一个基本的Flask应用结构from flask import Flask, render_template app Flask(__name__) app.route(/) def home(): return render_template(index.html) app.route(/movies/type) def show_movies(type): movies Movie.query.filter_by(typetype).all() return render_template(movies.html, moviesmovies, typetype) if __name__ __main__: app.run(debugTrue)4.2 前端模板设计使用Bootstrap快速搭建界面。创建templates/index.html!DOCTYPE html html head title个人电影推荐/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet /head body div classcontainer mt-4 h1 classtext-center我的电影推荐/h1 div classrow mt-4 div classcol-md-4 div classcard div classcard-body h5 classcard-title正在热映/h5 a href/movies/in_theaters classbtn btn-primary查看/a /div /div /div div classcol-md-4 div classcard div classcard-body h5 classcard-titleTop250/h5 a href/movies/top250 classbtn btn-primary查看/a /div /div /div div classcol-md-4 div classcard div classcard-body h5 classcard-title即将上映/h5 a href/movies/coming_soon classbtn btn-primary查看/a /div /div /div /div /div /body /html4.3 电影列表页面创建templates/movies.html展示具体电影列表!DOCTYPE html html head title{{ type }}电影/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet /head body div classcontainer mt-4 h1 classtext-center{{ type }}电影/h1 div classrow row-cols-1 row-cols-md-3 g-4 mt-4 {% for movie in movies %} div classcol div classcard h-100 img src{{ movie.image_url }} classcard-img-top alt{{ movie.title }} div classcard-body h5 classcard-title{{ movie.title }}/h5 p classcard-text small classtext-muted{{ movie.year }} | {{ movie.genres }}/smallbr 评分: {{ movie.rating }} /p /div /div /div {% endfor %} /div /div /body /html5. 进阶功能与优化5.1 实现简单的推荐算法基于用户评分数据我们可以实现一个简单的推荐系统。这里使用基于内容的推荐方法from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import linear_kernel def get_recommendations(movie_id, num_recommendations5): # 获取所有电影数据 movies Movie.query.all() # 准备数据 movie_data [] for m in movies: features f{m.genres} {m.directors} {m.casts} movie_data.append({ id: m.id, features: features, title: m.title }) # 创建TF-IDF矩阵 tfidf TfidfVectorizer(stop_wordsenglish) tfidf_matrix tfidf.fit_transform([m[features] for m in movie_data]) # 计算相似度 cosine_sim linear_kernel(tfidf_matrix, tfidf_matrix) # 找到目标电影的索引 indices {m[id]: i for i, m in enumerate(movie_data)} idx indices.get(movie_id) if idx is None: return [] # 获取相似度分数 sim_scores list(enumerate(cosine_sim[idx])) # 按分数排序 sim_scores sorted(sim_scores, keylambda x: x[1], reverseTrue) # 获取最相似的电影 sim_scores sim_scores[1:num_recommendations1] movie_indices [i[0] for i in sim_scores] return [movie_data[i][title] for i in movie_indices]5.2 添加搜索功能扩展Flask应用添加电影搜索功能app.route(/search) def search(): query request.args.get(q, ) if query: movies Movie.query.filter( Movie.title.contains(query) | Movie.original_title.contains(query) | Movie.directors.contains(query) | Movie.casts.contains(query) ).all() else: movies [] return render_template(search.html, moviesmovies, queryquery)对应的搜索页面模板templates/search.html!DOCTYPE html html head title电影搜索/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet /head body div classcontainer mt-4 h1 classtext-center电影搜索/h1 form classd-flex mt-4 action/search input classform-control me-2 typesearch nameq placeholder搜索电影... value{{ query }} button classbtn btn-outline-success typesubmit搜索/button /form {% if movies %} div classrow row-cols-1 row-cols-md-3 g-4 mt-4 {% for movie in movies %} div classcol div classcard h-100 img src{{ movie.image_url }} classcard-img-top alt{{ movie.title }} div classcard-body h5 classcard-title{{ movie.title }}/h5 p classcard-text small classtext-muted{{ movie.year }} | {{ movie.genres }}/smallbr 评分: {{ movie.rating }} /p /div /div /div {% endfor %} /div {% elif query %} div classalert alert-warning mt-4没有找到相关电影/div {% endif %} /div /body /html5.3 性能优化与缓存为了提高响应速度我们可以添加缓存机制from flask_caching import Cache cache Cache(app, config{CACHE_TYPE: simple}) app.route(/movies/type) cache.cached(timeout3600) # 缓存1小时 def show_movies(type): movies Movie.query.filter_by(typetype).all() return render_template(movies.html, moviesmovies, typetype)6. 部署与后续扩展6.1 项目部署完成开发后我们可以将项目部署到云服务器。推荐使用Gunicorn作为WSGI服务器pip install gunicorn gunicorn -w 4 -b 0.0.0.0:8000 app:app对于生产环境建议使用Nginx作为反向代理并配置SSL证书。6.2 可能的扩展方向这个基础项目还有很多可以扩展的地方用户系统添加用户注册登录功能记录用户偏好评分功能允许用户对电影评分实现个性化推荐定时任务设置定时任务自动更新电影数据移动端适配优化界面适应移动设备数据分析使用pandas和matplotlib分析电影数据趋势在实际项目中我发现最耗时的部分往往是数据的清洗和格式化。豆瓣API返回的数据结构虽然丰富但某些字段需要特别注意处理比如导演和演员信息是嵌套的JSON结构需要提取出我们需要的信息。另外API调用频率限制也是一个需要考虑的因素建议在代码中添加适当的延迟和错误处理。