最近在分析豆瓣电影TOP250榜单时,我发现单纯看列表很难发现数据背后的规律。于是决定用Python做一次完整的数据可视化分析,从爬取数据到生成交互式图表,整个过程收获颇丰。这个项目非常适合想学习数据分析的新手,也适合需要快速产出可视化报告的数据从业者。
爬取豆瓣TOP250页面时,需要注意几个关键点:
python复制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"
}
改进后的解析代码:
python复制for movie in movies:
# 处理主标题
title = movie.find('span', class_='title').text
# 处理副标题(可能不存在)
other_title = movie.find('span', class_='other')
other_title = other_title.text if other_title else ''
# 处理评分(注意异常处理)
try:
rating = float(movie.find('span', class_='rating_num').text)
except:
rating = None
python复制base_url = "https://movie.douban.com/top250?start={}"
for page in range(0, 250, 25):
url = base_url.format(page)
# 发送请求代码...
重要提示:豆瓣对频繁请求比较敏感,建议在请求间添加随机延时(如1-3秒),避免IP被封。
原始方案直接将数据存为CSV,但在实际项目中我推荐:
python复制if os.path.exists('douban_movies.csv'):
existing_df = pd.read_csv('douban_movies.csv')
# 过滤已存在的电影
else:
existing_df = pd.DataFrame()
# 合并新旧数据
df = pd.concat([existing_df, new_data]).drop_duplicates()
python复制df.to_csv('douban_movies.csv', index=False)
df.to_json('douban_movies.json', orient='records', force_ascii=False)
清洗前需要全面检查数据问题:
python复制print(df.isnull().sum())
python复制print(df.describe())
# 特别关注评分和年份的分布
python复制# 处理"(1994)"和"1994"两种格式
df['year'] = df['year'].str.extract(r'(\d{4})').astype(int)
python复制# 拆分类型字符串(如"剧情/爱情")
types = df['type'].str.get_dummies('/')
# 合并回原DataFrame
df = pd.concat([df, types], axis=1)
python复制# 将10分制转换为5星制
df['star_rating'] = df['rating'] / 2
python复制plt.figure(figsize=(12, 6))
sns.histplot(data=df, x='rating', bins=20, kde=True, hue='year_group')
plt.title('豆瓣TOP250评分分布(按年代分组)', fontsize=14)
plt.xlabel('评分', fontsize=12)
plt.ylabel('电影数量', fontsize=12)
plt.xticks(np.arange(7, 9.6, 0.5))
plt.grid(True, linestyle='--', alpha=0.7)
plt.savefig('rating_distribution.png', dpi=300, bbox_inches='tight')
python复制plt.figure(figsize=(14, 7))
sns.boxplot(data=df, x='decade', y='rating', palette='viridis')
plt.title('不同年代电影评分分布', fontsize=14)
plt.xlabel('年代', fontsize=12)
plt.ylabel('评分', fontsize=12)
plt.xticks(rotation=45)
python复制# 统计导演作品数量和平均分
director_stats = df.groupby('director').agg(
movie_count=('title', 'count'),
avg_rating=('rating', 'mean')
).sort_values('movie_count', ascending=False).head(10)
python复制import plotly.graph_objects as go
# 构建类型流向数据
# ...数据处理代码...
fig = go.Figure(go.Sankey(
node=dict(
pad=15,
thickness=20,
line=dict(color="black", width=0.5),
label=labels,
color=colors
),
link=dict(
source=source,
target=target,
value=value
)
))
fig.update_layout(title_text="电影类型关联分析", font_size=10)
fig.write_html('type_relation.html')
python复制import plotly.express as px
fig = px.scatter(df, x='year', y='rating',
hover_data=['title', 'director', 'type'],
color='type_group',
size='vote_count',
trendline="lowess")
fig.update_layout(
hovermode='closest',
title='豆瓣TOP250年代-评分分布',
xaxis_title="年份",
yaxis_title="评分"
)
fig.show()
python复制fig = px.parallel_categories(df,
dimensions=['decade', 'type_group', 'rating_group'],
color="rating",
labels={'decade':'年代', 'type_group':'类型', 'rating_group':'评分区间'},
title="电影多维特征分析")
python复制from pyecharts import options as opts
from pyecharts.charts import Geo
# 处理地区数据
# ...数据预处理...
geo = (
Geo()
.add_schema(maptype="china")
.add("电影数量", data_pair=location_data, type_="scatter")
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
visualmap_opts=opts.VisualMapOpts(max_=max_count),
title_opts=opts.TitleOpts(title="电影地区分布")
)
)
geo.render("movie_location.html")
推荐使用Jupyter Notebook整合分析过程:
使用Python自动化生成PDF报告:
python复制from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
# 添加标题
pdf.cell(200, 10, txt="豆瓣TOP250分析报告", ln=1, align='C')
# 添加分析文本
pdf.multi_cell(0, 10, txt=analysis_text)
# 插入图表
pdf.image('rating_distribution.png', x=10, y=None, w=180)
pdf.output("douban_analysis.pdf")
在实际操作中,我总结了几个关键经验:
这个项目完整展示了从数据获取到分析可视化的全流程,其中的技术栈和解决方案可以复用到其他类似的数据分析场景中。对于想深入学习的同学,我建议尝试: