在电影爱好者和数据分析师眼中,豆瓣电影 Top250 是一个极具价值的数据宝库。通过 Python 爬虫技术,不仅能抓取到电影的基本评分数据,还能深入挖掘评分变化趋势、观众评论倾向等多维度信息。本文将从爬虫环境搭建、目标网页分析、动态折线图绘制等关键环节,详细讲解如何实现豆瓣电影 Top250 的数据抓取与分析。
豆瓣电影 Top250 榜单是根据用户评分生成的高口碑电影列表,涵盖了不同时期、不同类型的经典影片。随着电影市场的不断发展和观众审美偏好的变化,榜单中电影的评分数据也呈现出动态变化趋势。通过爬取并分析这些数据,可以洞察电影市场的发展脉络和观众的审美变化。
确保本地已安装 Python 3.6 或以上版本,可从 Python 官网下载安装。
使用 pip 工具安装以下库:
pip install requests beautifulsoup4 pandas matplotlib
访问 豆瓣电影 Top250 页面,按 F12 打开开发者工具,观察电影条目在 HTML 中的位置和结构。
标签中。
标签中。 标签内的第二个
标签中,需去掉 “人评价” 文字。
- 经典台词 :位于
标签中,部分电影可能没有经典台词。
(三)分页规律
榜单共 250 条,每页 25 条,需循环 10 次,修改 URL 中的 start
参数,依次为 0, 25, 50,..., 225
。
四、构建爬虫代码
(一)发送 HTTP 请求
import requests
from bs4 import BeautifulSoup
import time
# 设置请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
def get_movies(page):
url = f'https://movie.douban.com/top250?start={page * 25}'
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # 检查请求是否成功
return response.text
except requests.exceptions.RequestException as e:
print(f"请求第 {page + 1} 页出错: {e}")
return None
(二)解析 HTML 并提取数据
def parse_movies(html):
soup = BeautifulSoup(html, 'html.parser')
items = soup.find_all('div', class_='item')
movies = []
for item in items:
title = item.find('span', class_='title').text
rating = item.find('span', class_='rating_num').text
comment_tag = item.find('div', class_='star').find_all('span')[-1]
comment_num = comment_tag.text[:-3] # 去掉 "人评价" 文字
inq = item.find('span', class_='inq')
quote = inq.text if inq else '暂无经典台词'
movies.append({
'电影名称': title,
'评分': rating,
'评论人数': comment_num,
'经典台词': quote
})
return movies
all_movies = []
for page in range(10): # 共 10 页
html = get_movies(page)
if html:
all_movies.extend(parse_movies(html))
time.sleep(3) # 每页间隔 3 秒,避免请求过于频繁
(三)代码说明
- 请求头设置 :通过设置
User-Agent
模拟浏览器访问,提高爬虫的成功率和稳定性。
- 循环请求分页数据 :通过修改
start
参数循环获取每页数据,并在每次请求后添加延迟,避免触发反爬虫机制。
- HTML 解析与数据提取 :使用 BeautifulSoup 的
find
和 find_all
方法定位并提取电影标题、评分、评论人数、经典台词等信息。
五、数据存储与预处理
(一)存储为 CSV 文件
import pandas as pd
# 将数据转换为 DataFrame
df = pd.DataFrame(all_movies)
# 保存为 CSV 文件
df.to_csv('douban_top250.csv', index=False, encoding='utf-8-sig')
print("数据已成功保存为 douban_top250.csv")
(二)数据清洗
- 处理缺失值 :检查数据中是否存在缺失值,如有缺失值可根据具体情况选择填充或删除。
- 数据类型转换 :将评分和评论人数转换为数值类型,方便后续数据分析。
# 转换评分和评论人数为数值类型
df['评分'] = pd.to_numeric(df['评分'])
df['评论人数'] = pd.to_numeric(df['评论人数'])
六、绘制动态折线图
(一)绘制静态评分趋势折线图
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 按评分排序并重置索引
df_sorted = df.sort_values(by='评分', ascending=False).reset_index(drop=True)
# 绘制静态折线图
plt.figure(figsize=(12, 8))
plt.plot(df_sorted.index, df_sorted['评分'], marker='o', linestyle='-', color='b')
plt.xlabel('电影排名')
plt.ylabel('评分')
plt.title('豆瓣电影 Top250 评分趋势')
plt.grid(True)
plt.show()
(二)实现评分趋势动态折线图
# 动态折线图绘制
fig, ax = plt.subplots(figsize=(12, 8))
x_data = []
y_data = []
line, = ax.plot([], [], marker='o', linestyle='-', color='b')
def init():
ax.set_xlim(0, len(df_sorted))
ax.set_ylim(0, 10)
ax.set_xlabel('电影排名')
ax.set_ylabel('评分')
ax.set_title('豆瓣电影 Top250 评分趋势(动态)')
ax.grid(True)
return line,
def update(frame):
x_data.append(frame)
y_data.append(df_sorted['评分'][frame])
line.set_data(x_data, y_data)
ax.set_xlim(0, frame + 1)
return line,
ani = FuncAnimation(fig, update, frames=len(df_sorted), init_func=init, blit=True)
ani.save('douban_top250_rating_trend.gif', writer='pillow')
plt.show()
(三)代码说明
- 静态折线图 :使用 matplotlib 的
plot
函数绘制评分随电影排名变化的折线图,直观展示整体评分趋势。
- 动态折线图 :通过
FuncAnimation
类实现动态效果,逐个绘制每部电影的评分数据点,最终形成完整的评分趋势折线图,并保存为 GIF 文件。
七、爬虫的优化与拓展
(一)优化爬虫性能
- 增加延迟和错误重试机制 :在网络请求中增加随机延迟,并对失败的请求进行有限次数的重试,提高爬虫的稳定性和成功率。
def get_movies_with_retry(page, max_retries=3):
url = f'https://movie.douban.com/top250?start={page * 25}'
retries = 0
while retries < max_retries:
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.text
except requests.exceptions.RequestException as e:
print(f"请求第 {page + 1} 页失败,正在重试 ({retries + 1}/{max_retries}): {e}")
retries += 1
time.sleep(5) # 增加延迟,避免频繁请求
print(f"请求第 {page + 1} 页失败,已达到最大重试次数")
return None
- 使用代理池 :在爬取过程中使用代理 IP 池,避免因频繁请求同一 IP 导致被封禁。
(二)拓展爬虫功能
- 爬取更多电影信息 :除了基本的电影名称、评分、评论人数、经典台词外,还可以爬取导演、主演、上映年份、电影类型等详细信息。
def parse_detailed_movies(html):
soup = BeautifulSoup(html, 'html.parser')
items = soup.find_all('div', class_='item')
movies = []
for item in items:
title = item.find('span', class_='title').text
rating = item.find('span', class_='rating_num').text
comment_tag = item.find('div', class_='star').find_all('span')[-1]
comment_num = comment_tag.text[:-3]
inq = item.find('span', class_='inq')
quote = inq.text if inq else '暂无经典台词'
# 爬取导演信息
info = item.find('div', class_='info')
director = info.find('div', class_='bd').find('p').text.split('\n')[0].split(' ')[-1]
# 爬取主演信息
actors = info.find('div', class_='bd').find('p').text.split('\n')[0].split('主演: ')[-1].split(' ')[0]
# 爬取上映年份
year = info.find('div', class_='bd').find('p').text.split('\n')[1].strip().split(' ')[-1]
# 爬取电影类型
movie_type = info.find('div', class_='bd').find('p').text.split('\n')[1].strip().split(' ')[0]
movies.append({
'电影名称': title,
'评分': rating,
'评论人数': comment_num,
'经典台词': quote,
'导演': director,
'主演': actors,
'上映年份': year,
'电影类型': movie_type
})
return movies
- 拓展数据可视化维度 :除了评分趋势折线图外,还可以绘制评分分布直方图、评分与评论人数关系散点图、不同电影类型的数量分布图等,从多个角度分析电影数据。
import seaborn as sns
# 绘制评分分布直方图
plt.figure(figsize=(10, 6))
sns.histplot(data=df, x='评分', bins=10, kde=True)
plt.title('豆瓣电影 Top250 评分分布')
plt.xlabel('评分')
plt.ylabel('电影数量')
plt.show()
# 绘制评分与评论人数关系散点图
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='评分', y='评论人数')
plt.title('豆瓣电影 Top250 评分与评论人数关系')
plt.xlabel('评分')
plt.ylabel('评论人数')
plt.show()
# 绘制电影类型数量分布图
plt.figure(figsize=(12, 8))
movie_type_counts = df['电影类型'].value_counts()
sns.barplot(x=movie_type_counts.index, y=movie_type_counts.values)
plt.title('豆瓣电影 Top250 电影类型数量分布')
plt.xlabel('电影类型')
plt.ylabel('数量')
plt.xticks(rotation=45)
plt.show()
八、项目总结与拓展
(一)项目总结
本文详细讲解了如何利用 Python 爬虫技术抓取豆瓣电影 Top250 的数据,并进行数据清洗、存储和可视化分析。从爬虫环境搭建、目标网页分析,到数据解析与动态折线图绘制,每个环节都进行了细致的说明和代码示例展示。通过这些步骤,成功实现了豆瓣电影 Top250 评分趋势的可视化展示,为电影爱好者和数据分析师提供了一个直观了解电影评分变化的工具。