在影视行业数字化浪潮中,豆瓣电影作为国内最具影响力的评分与评论平台之一,沉淀了海量电影数据。本文基于Python构建了一套从数据爬取、清洗到分析与可视化的完整系统,帮助读者深入挖掘电影评分分布、类型趋势、导演/演员影响力等核心信息。无论你是电影爱好者还是行业从业者,都能从中获得数据参考与趋势洞察。
一、项目背景与核心目标
本项目旨在通过Python技术栈实现豆瓣电影数据的自动化采集与多维度分析,最终以交互式可视化图表呈现洞察结果。核心能力包括:
- 自动化采集:抓取豆瓣电影的基础信息、评分、评论及影片详情等字段。
- 结构化存储:将数据清洗后存储至CSV与MySQL数据库。
- 统计分析与可视化:针对导演/演员、地区、类型、评分等维度进行深度统计,并基于Echarts/Matplotlib/Seaborn生成交互式图表。
值得注意的是,虽然本系统以Python为主力语言,但类似的数据处理流程在Java、JavaScript、C++、Go等语言中同样可以复现(例如用Java的Jsoup库做爬虫,或用Go的Colly框架)。掌握一种语言的数据工程思维,便能举一反三。
二、核心数据维度说明
爬取的豆瓣电影核心字段涵盖基础信息、详情与评论三大类,具体如下:
| 字段分类 | 具体字段 | 说明 |
|---|---|---|
| 基础信息 | 电影名、评分、封面图、详情 URL、上映时间 | 核心标识与基础属性 |
| 制作信息 | 导演、主演、类型、制片国家 / 地区、语言、片长 | 影片制作维度特征 |
| 评价信息 | 星星评分比例、评价人数、前五条热评、评论时间 | 观众反馈维度 |
| 补充信息 | 电影简介、预告片、5 张详情图片 | 内容补充维度 |
这些字段为后续分析提供了丰富素材。例如,通过“导演”和“主演”字段可以统计个人作品数量;借助“制片地区”和“语言”字段能分析地域分布趋势。
三、核心技术实现流程
3.1 数据采集:豆瓣电影数据爬取
数据采集是整个流程的起点。我们使用Requests库请求豆瓣电影列表页与详情页,结合BeautifulSoup解析HTML结构,提取目标字段。针对数组类型字段(如导演、主演、类型),需进行格式化处理,最终标准化为可存储的结构。
以下是核心爬取代码的优化版:
import requests
import re
import json
import random
from bs4 import BeautifulSoup
# 请求头(模拟浏览器,避免反爬)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
}
def crawl_movie_data(detailUrls, moveisInfomation):
"""
爬取豆瓣电影详情数据
:param detailUrls: 电影详情页URL列表
:param moveisInfomation: 列表页提取的基础信息列表
:return: 结构化的电影数据列表
"""
result_list = []
for i, move_info in enumerate(moveisInfomation):
try:
result_data = {}
# 列表页基础信息提取
result_data['detailLink'] = detailUrls[i] # 详情页URL
result_data['directors'] = ','.join(move_info['directors']) # 导演(转字符串存储)
result_data['rate'] = move_info['rate'] # 评分
result_data['title'] = move_info['title'] # 影片名
result_data['casts'] = ','.join(move_info['casts']) # 主演(转字符串存储)
result_data['cover'] = move_info['cover'] # 封面图URL
# 详情页深度信息提取
detail_res = requests.get(detailUrls[i], headers=headers, timeout=10)
detail_res.encoding = 'utf-8'
soup = BeautifulSoup(detail_res.text, 'lxml')
# 上映年份(正则提取)
year_elem = soup.find('span', class_='year')
result_data['year'] = re.findall(r'[(](.*?)[)]', year_elem.get_text())[0] if year_elem else ''
# 影片类型
type_elems = soup.find_all('span', property='v:genre')
types = [elem.get_text() for elem in type_elems]
result_data['types'] = ','.join(types)
# 制片国家/地区
country_elem = soup.find_all('span', class_='pl')[4].next_sibling.strip()
country = [c.strip() for c in country_elem.split('/')]
result_data['country'] = ','.join(country)
# 影片语言
lang_elem = soup.find_all('span', class_='pl')[5].next_sibling.strip()
lang = [l.strip() for l in lang_elem.split('/')]
result_data['lang'] = ','.join(lang)
# 上映时间(正则提取日期)
uptime_elems = soup.find_all('span', property='v:initialReleaseDate')
uptime_str = ''.join([elem.get_text() for elem in uptime_elems])
uptime = re.findall(r'\d*-\d*-\d*', uptime_str)[0] if uptime_str else ''
result_data['time'] = uptime
# 影片时长(无数据则随机填充合理范围)
runtime_elem = soup.find('span', property='v:runtime')
if runtime_elem:
result_data['movieTime'] = re.findall(r'\d+', runtime_elem.get_text())[0]
else:
result_data['movieTime'] = str(random.randint(80, 180)) # 修正原代码moveiTime拼写错误
# 评价人数
comment_len_elem = soup.find('span', property='v:votes')
result_data['comment_len'] = comment_len_elem.get_text() if comment_len_elem else '0'
# 星星评分比例
star_elems = soup.find_all('span', class_='rating_per')
stars = [elem.get_text() for elem in star_elems]
result_data['starts'] = ','.join(stars)
# 影片简介
summary_elem = soup.find('span', property='v:summary')
result_data['summary'] = summary_elem.get_text().strip() if summary_elem else ''
# 前五条热评(结构化存储)
comments = []
comment_info_elems = soup.find_all('span', class_='comment-info')[:5]
comment_content_elems = soup.find_all('span', class_='short')[:5]
for idx, (info_elem, content_elem) in enumerate(zip(comment_info_elems, comment_content_elems)):
comment = {}
comment['user'] = info_elem.contents[1].get_text() if len(info_elem.contents) > 1 else ''
comment['star'] = re.findall(r'(\d*)', info_elem.contents[5].attrs['class'][0])[7] if len(info_elem.contents) > 5 else ''
comment['time'] = info_elem.contents[7].attrs['title'] if len(info_elem.contents) > 7 else ''
comment['content'] = content_elem.get_text()
comments.append(comment)
result_data['comments'] = json.dumps(comments, ensure_ascii=False)
# 5张详情图片
img_elems = soup.select('.related-pic-bd img')
img_list = [elem['src'] for elem in img_elems[:5]]
result_data['imgList'] = ','.join(img_list)
result_list.append(result_data)
except Exception as e:
print(f"爬取第{i}条数据失败:{str(e)}")
continue
return result_list
⚠️ 注意事项:豆瓣有较强的反爬机制,建议在正式环境中加入IP代理池、随机User-Agent和请求延迟,以降低被封风险。如果使用Java或Go实现爬虫,可借助OkHttp或Colly框架处理类似的请求与解析逻辑。
3.2 数据存储与结构化
爬取完成后,将结构化数据同步存储至CSV文件与MySQL数据库,并记录爬取页数以避免重复。核心逻辑如下:
- CSV存储:利用Pandas的
to_csv方法,指定编码为utf-8-sig(解决中文乱码)。 - MySQL存储:通过
pymysql库建立连接,批量插入数据以提升效率。 如果数据量极大(如百万级),可考虑改用Go的并发写入或Java的JDBC批处理。
接着,将清洗后的数据转换为Pandas DataFrame,方便后续分析。修正原代码中Pandas别名不规范(ps改为通用的pd)及字段拼写错误等问题:
from . import homeData
import pandas as pd
# 构建DataFrame(标准化字段名与顺序)
df = pd.DataFrame(
homeData.getAllData(),
columns=[
'id', 'directors', 'rate', 'title', 'casts', 'cover', 'year', 'types',
'country', 'lang', 'time', 'movieTime', 'comment_len', 'starts',
'summary', 'comments', 'imgList', 'movieUrl', 'detailLink'
]
)
# 数据清洗:空值填充、类型转换
df['rate'] = pd.to_numeric(df['rate'], errors='coerce').fillna(0) # 评分转数值,空值填0
df['comment_len'] = pd.to_numeric(df['comment_len'], errors='coerce').fillna(0) # 评价人数转数值
df['movieTime'] = pd.to_numeric(df['movieTime'], errors='coerce').fillna(0) # 时长转数值
3.3 数据分析:核心统计函数实现
针对导演/演员作品数量、制片地区分布等核心维度,实现统计函数,为可视化提供数据支撑。以下为三个关键函数:
演员参演电影数量统计
def get_actor_movie_count():
"""
统计参演电影数量TOP20的演员
:return: 演员名列表、对应作品数量列表
"""
# 拆分演员字段(原存储为逗号分隔字符串)
actor_series = df['casts'].str.split(',', expand=True).stack()
actor_count = actor_series.value_counts() # 统计每个演员出现次数
top20_actors = actor_count[-20:] # 取TOP20
# 拆分x/y轴数据
x = top20_actors.index.tolist()
y = top20_actors.values.tolist()
return x, y
导演执导电影数量统计
def get_director_movie_count():
"""
统计执导电影数量TOP20的导演
:return: 导演名列表、对应作品数量列表
"""
# 拆分导演字段(原存储为逗号分隔字符串)
director_series = df['directors'].str.split(',', expand=True).stack()
director_count = director_series.value_counts() # 统计每个导演出现次数
top20_directors = director_count[-20:] # 取TOP20
# 拆分x/y轴数据
x = top20_directors.index.tolist()
y = top20_directors.values.tolist()
return x, y
制片地区分布统计
def get_country_distribution():
"""
统计各制片地区的电影数量
:return: 地区列表、对应数量列表
"""
# 拆分地区字段(原存储为逗号分隔字符串)
country_series = df['country'].str.split(',', expand=True).stack()
country_count = country_series.value_counts()
# 拆分x/y轴数据
x = country_count.index.tolist()
y = country_count.values.tolist()
return x, y
这些函数返回的DataFrame可直接用于绘图。例如,统计导演作品数量后,可以筛选出前20名进行柱状图展示。
3.4 数据可视化:多维度洞察
基于上述统计结果,结合Echarts(前端交互式可视化)与Matplotlib/Seaborn(Python静态可视化)实现多维度展示:
- 导演/演员TOP20:横向柱状图,对比数量差异。
- 制片地区分布:饼图,展示占比。
- 电影评分分布:直方图,分析评分区间分布。
- 类型与评分关联:热力图,展示不同类型电影的评分均值。
- 评论数与评分关联:散点图,分析热度与口碑的关系。
示例(Matplotlib实现导演TOP20可视化):
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False
# 获取统计数据
directors, counts = get_director_movie_count()
# 绘制横向柱状图
fig, ax = plt.subplots(figsize=(12, 8))
ax.barh(directors, counts, color='#1f77b4')
ax.set_xlabel('执导电影数量')
ax.set_ylabel('导演')
ax.set_title('豆瓣电影导演执导数量TOP20')
plt.tight_layout()
plt.savefig('director_top20.png', dpi=300)
plt.show()
如果你希望实现更动态的交互效果,可以将数据导出为JSON,在前端用Echarts或D3.js渲染。对于熟悉JavaScript的开发者,这比纯Python方案更具灵活性。
[AFFILIATE_SLOT_1]四、项目总结与拓展方向
4.1 项目亮点
- 全流程自动化:从爬取、清洗到分析可视化,实现端到端自动化。
- 数据标准化:统一字段格式,处理空值/异常值,保证分析准确性。
- 多维度分析:覆盖基础信息、制作维度、观众反馈等核心维度。
- 可视化交互性:结合Echarts实现前端交互式展示,支持自定义筛选。
4.2 可拓展方向
- 反爬优化:加入IP代理池、请求延迟随机化,提升爬取稳定性。✅ 可参考Go的Colly框架中的限流机制。
- 更多分析维度:新增“上映时间与评分”“演员与评分关联”等分析。
- 可视化升级:接入Dash/Streamlit构建Web可视化平台,支持实时查询。 如果团队熟悉Java,也可用Spring Boot + Thymeleaf实现类似功能。
- 数据挖掘:基于机器学习实现电影评分预测、类型推荐等功能。
五、技术栈总结
| 技术分类 | 核心工具 / 库 | 用途 |
|---|---|---|
| 数据采集 | Requests、BeautifulSoup、re | 网页请求、HTML 解析、正则提取 |
| 数据处理 | Pandas、NumPy | 结构化清洗、数值计算 |
| 数据存储 | CSV、MySQL(pymysql) | 本地 / 数据库存储 |
| 可视化 | Matplotlib、Seaborn、Echarts | 静态 / 交互式可视化 |
| 辅助工具 | json、random | 数据格式化、异常值填充 |
六、系统运行与界面展示
数据库表信息如下:


请修改为自己的数据库主机名和账号密码:

启动项目:

服务端口:5000
- 首页:http://127.0.0.1:5000
- 用户注册:http://127.0.0.1:5000/registry

用户登录:

首页页面展示:

电影数据(包括电影名、评分、片场、预告片等):

查看电影预告片:

电影搜索:

电影产量分析:

电影数据时长分布占比:

电影评分统计分析:


豆瓣评分星级饼状图、豆瓣年度评价评分柱状图:

豆瓣电影中外评分分布图:

数据视图切换:


电影拍摄地点统计图:

电影语言统计图:

电影类型饼图:

导演作品数量前20:


数据表操作:

标题词云图:

简介词云图:

演员名词云图:

评论词云图:


通过本项目,完整落地了“数据采集→处理→分析→可视化”的大数据分析流程。你不仅掌握了Python爬虫与数据分析的核心技能,也实现了对豆瓣电影数据的深度洞察。这套技术框架同样适用于其他影视平台或电商评论分析,只需调整爬虫目标与字段即可。如果对源码或商业合作感兴趣,欢迎留言交流!
浙公网安备 33010602011771号