用Python爬取豆瓣电影API数据,手把手教你搭建个人电影推荐小站
用Python构建个人电影推荐系统的全流程实战
最近几年,电影推荐系统变得越来越受欢迎。作为一个电影爱好者,你是否想过自己动手搭建一个个性化的电影推荐平台?本文将带你从零开始,使用Python和豆瓣API构建一个完整的电影推荐系统。不同于简单的API调用教程,我们会涵盖数据获取、处理、存储到可视化展示的全流程,最终打造一个可以实际使用的个人电影看板。
1. 准备工作与环境搭建
在开始之前,我们需要准备好开发环境。推荐使用Python 3.8或更高版本,并安装以下必要的库:
pip install requests pandas flask flask-sqlalchemy
- requests :用于发送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="北京", start=0, count=10):
url = "https://api.douban.com/v2/movie/in_theaters"
params = {
"apikey": "你的API_KEY",
"city": city,
"start": start,
"count": count
}
response = requests.get(url, params=params)
if response.status_code == 200:
return response.json()
else:
print(f"请求失败,状态码:{response.status_code}")
return None
2.2 数据结构解析
豆瓣API返回的是JSON格式的数据,我们需要了解其结构才能有效提取信息。以热映电影接口为例,返回数据主要包含以下字段:
| 字段名 | 类型 | 描述 |
|---|---|---|
| count | int | 当前返回的电影数量 |
| start | int | 起始位置 |
| total | int | 总电影数量 |
| subjects | list | 电影列表 |
每个电影对象又包含丰富的信息:
{
"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(start=0, count=10):
url = "https://api.douban.com/v2/movie/top250"
params = {
"apikey": "你的API_KEY",
"start": start,
"count": count
}
return requests.get(url, params=params).json()
def get_coming_soon(start=0, count=10):
url = "https://api.douban.com/v2/movie/coming_soon"
params = {
"apikey": "你的API_KEY",
"start": start,
"count": count
}
return requests.get(url, params=params).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_key=True)
title = db.Column(db.String(100), nullable=False)
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 f'<Movie {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(
id=movie['id'],
title=movie['title'],
original_title=movie.get('original_title', ''),
year=movie.get('year', ''),
genres=','.join(movie.get('genres', [])),
rating=movie.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_url=movie.get('images', {}).get('large', ''),
type=movie_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(type=type).all()
return render_template('movies.html', movies=movies, type=type)
if __name__ == '__main__':
app.run(debug=True)
4.2 前端模板设计
使用Bootstrap快速搭建界面。创建 templates/index.html :
<!DOCTYPE html>
<html>
<head>
<title>个人电影推荐</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 class="text-center">我的电影推荐</h1>
<div class="row mt-4">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">正在热映</h5>
<a href="/movies/in_theaters" class="btn btn-primary">查看</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">Top250</h5>
<a href="/movies/top250" class="btn btn-primary">查看</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">即将上映</h5>
<a href="/movies/coming_soon" class="btn btn-primary">查看</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
4.3 电影列表页面
创建 templates/movies.html 展示具体电影列表:
<!DOCTYPE html>
<html>
<head>
<title>{{ type }}电影</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 class="text-center">{{ type }}电影</h1>
<div class="row row-cols-1 row-cols-md-3 g-4 mt-4">
{% for movie in movies %}
<div class="col">
<div class="card h-100">
<img src="{{ movie.image_url }}" class="card-img-top" alt="{{ movie.title }}">
<div class="card-body">
<h5 class="card-title">{{ movie.title }}</h5>
<p class="card-text">
<small class="text-muted">{{ movie.year }} | {{ movie.genres }}</small><br>
评分: {{ movie.rating }}
</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</body>
</html>
5. 进阶功能与优化
5.1 实现简单的推荐算法
基于用户评分数据,我们可以实现一个简单的推荐系统。这里使用基于内容的推荐方法:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import linear_kernel
def get_recommendations(movie_id, num_recommendations=5):
# 获取所有电影数据
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_words='english')
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, key=lambda x: x[1], reverse=True)
# 获取最相似的电影
sim_scores = sim_scores[1:num_recommendations+1]
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', movies=movies, query=query)
对应的搜索页面模板 templates/search.html :
<!DOCTYPE html>
<html>
<head>
<title>电影搜索</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 class="text-center">电影搜索</h1>
<form class="d-flex mt-4" action="/search">
<input class="form-control me-2" type="search" name="q" placeholder="搜索电影..." value="{{ query }}">
<button class="btn btn-outline-success" type="submit">搜索</button>
</form>
{% if movies %}
<div class="row row-cols-1 row-cols-md-3 g-4 mt-4">
{% for movie in movies %}
<div class="col">
<div class="card h-100">
<img src="{{ movie.image_url }}" class="card-img-top" alt="{{ movie.title }}">
<div class="card-body">
<h5 class="card-title">{{ movie.title }}</h5>
<p class="card-text">
<small class="text-muted">{{ movie.year }} | {{ movie.genres }}</small><br>
评分: {{ movie.rating }}
</p>
</div>
</div>
</div>
{% endfor %}
</div>
{% elif query %}
<div class="alert alert-warning mt-4">没有找到相关电影</div>
{% endif %}
</div>
</body>
</html>
5.3 性能优化与缓存
为了提高响应速度,我们可以添加缓存机制:
from flask_caching import Cache
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
@app.route('/movies/<type>')
@cache.cached(timeout=3600) # 缓存1小时
def show_movies(type):
movies = Movie.query.filter_by(type=type).all()
return render_template('movies.html', movies=movies, type=type)
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调用频率限制也是一个需要考虑的因素,建议在代码中添加适当的延迟和错误处理。
更多推荐



所有评论(0)