高分电影全链路数据分析
TMDb 高分电影数据全链路分析项目报告
作者:黄思博
技术栈:Python (requests/lxml/pandas/sklearn/jieba/wordcloud) | MySQL | DataGrip | Tableau Public | Kaggle
数据来源:TMDb Top-Rated 榜单(1000 条)+ Kaggle TMDB & IMDB Merged Dataset(68 万条)
项目周期:约 8 周
一、项目背景与目标
TMDb(The Movie Database)是全球最具影响力的电影数据库之一。本次项目以“高分电影特征分析”为核心,回答以下问题:
- 高分电影有什么共性?(时长、类型、语言、导演等)
- 电影产量与质量随时间如何变化?
- 英语片与非英语片的表现有何差异?
- 能否用机器学习预测一部电影是否属于高分?
项目经历从网页爬虫 → 数据清洗 → SQL 分析 → Tableau 可视化 → 文本挖掘 → 机器学习预测的完整链路,最终形成一份可落地的数据分析作品。
二、第一阶段:网页爬虫获取数据
2.1 技术方案
使用 Python 的 requests + lxml 编写爬虫,抓取 TMDb Top-Rated 榜单前 1000 部电影的详情页数据。
目标字段(11 个):电影名称、年份、上映日期、类型、时长、评分、语言、导演、标语、简介、详情页 URL。
2.2 关键技术点
- 列表页与详情页分离抓取:先抓列表页拿到 1000 部电影的详情页 URL,再逐个访问详情页提取字段。
- XPath 定位:用 Chrome DevTools 分析 HTML 结构,定位到评分组件
consensus_pill、时长组件、语言组件等。 - 随机延迟防封:每个请求间隔
random.uniform(1, 2)秒。 - 多 XPath 兜底:评分人数尝试从
data-count属性、consensus_pill文本、全文正则三种方式提取。 - Session + 自动重试:使用
requests.Session复用连接,配置urllib3.Retry自动重试 429/5xx 错误。
2.3 遇到的问题
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 评分人数抓取失败 | 全部为 0 | rating_details_window 是 JS 动态弹窗,静态 HTML 中不存在 |
确认静态请求无法获取后,将该字段标记为缺失,后续改用 Kaggle 数据集中的 vote_count 字段补全 |
| IP 被限制 | 403 Forbidden、ConnectionResetError | 请求频率触发 TMDb 反爬机制 | 每个请求间隔 random.uniform(1, 2) 秒;使用 requests.Session 复用连接;配置 urllib3.Retry 自动重试 429/5xx 错误;若持续 403 则暂停抓取并降低请求频率 |
| API 访问失败 | api.themoviedb.org 超时 |
该域名在国内被墙 | 注册 TMDb 官方 API 后仍无法访问;尝试 Cloudflare Workers 代理,但 workers.dev 域名同样被墙;最终放弃 API 方案,改用 Kaggle 公开数据集 TMDB + IMDB Merged Movies Dataset 完成后续分析与建模 |
2.4 最终产出
成功抓取 1000 条电影数据,字段完整率约 98%(仅评分人数缺失),保存为 csv_data/movie_list.csv。

三、第二阶段:Pandas 数据清洗
3.1 清洗任务
| 字段 | 原始格式 | 清洗后格式 |
|---|---|---|
| 电影评分 | "79"(0–100) |
7.9(0–10) |
| 电影年份 | "(1994)" |
1994(整数) |
| 电影时长 | "2h 22m" / "142 分钟" |
142(整数分钟) |
| 类型 | "剧情,史诗" |
拆分为多行存入 movie_genres 表 |
| 语言 | "$20,000,000"(异常值) |
剔除异常值,保留有效语言 |
3.2 关键代码
def parse_rating(text):
return round(float(text) / 10, 1) if text else None
def parse_runtime(text):
text = text.strip().lower()
m = re.match(r"(?:(\d+)\s*h)?\s*(?:(\d+)\s*m)?", text)
if m and (m.group(1) or m.group(2)):
h = int(m.group(1)) if m.group(1) else 0
mnt = int(m.group(2)) if m.group(2) else 0
return h * 60 + mnt
m = re.search(r"(\d+)", text)
return int(m.group(1)) if m else None
3.3 数据入库
使用 Datagrip 将清洗后的数据导入 MySQL,建立两张表:
CREATE TABLE movies (
movie_id INT PRIMARY KEY,
title VARCHAR(255),
year INT,
release_date DATE,
runtime_min INT,
rating DECIMAL(3,1),
language VARCHAR(100),
director VARCHAR(255),
tagline TEXT,
overview TEXT,
url VARCHAR(255)
);
CREATE TABLE movie_genres (
movie_id INT,
genre VARCHAR(50),
PRIMARY KEY (movie_id, genre),
FOREIGN KEY (movie_id) REFERENCES movies(movie_id)
);
3.4 清洗后数据展示

四、第三阶段:DataGrip SQL 筛选分析
4.1 数据质量检查
SELECT
COUNT(*) AS total,
SUM(year IS NULL) AS missing_year,
SUM(rating IS NULL) AS missing_rating,
SUM(runtime_min IS NULL) AS missing_runtime
FROM movies;
结果:1000 条数据,缺失时长 17 条(1.7%),其余字段完整。
4.2 核心分析 SQL
(1)评分分层
SELECT
CASE
WHEN rating >= 8.5 THEN '超高分(>=8.5)'
WHEN rating >= 8.0 THEN '高分(8.0-8.5)'
WHEN rating >= 7.5 THEN '中高(7.5-8.0)'
ELSE '中分(<7.5)'
END AS rating_level,
COUNT(*) AS cnt,
ROUND(AVG(runtime_min), 0) AS avg_runtime,
ROUND(AVG(year), 0) AS avg_year
FROM movies
WHERE rating IS NOT NULL
GROUP BY rating_level;
结果:
| 评分档 | 数量 | 平均时长 | 平均年份 |
|---|---|---|---|
| 超高分(>=8.5) | 30 | 143 分钟 | 2002 |
| 高分(8.0-8.5) | 372 | 119 分钟 | 1999 |
| 中高(7.5-8.0) | 598 | 112 分钟 | 1997 |
(2)年代趋势
SELECT
FLOOR(year / 10) * 10 AS decade,
COUNT(*) AS movie_count,
ROUND(AVG(rating), 2) AS avg_rating,
ROUND(AVG(runtime_min), 0) AS avg_runtime
FROM movies
WHERE year BETWEEN 1920 AND 2025
GROUP BY decade
ORDER BY decade;
(3)语言差异
SELECT
language,
COUNT(*) AS cnt,
ROUND(AVG(rating), 2) AS avg_rating
FROM movies
WHERE language IS NOT NULL AND language != ''
GROUP BY language
HAVING cnt >= 5
ORDER BY avg_rating DESC;
结果:日语(7.99)、西班牙语(7.98)、朝鲜语(7.98)、意大利语(7.98)均高于英语(7.94)。
4.3 核心发现
- 时长与评分正相关:超高分电影平均 143 分钟,比中高片多 31 分钟。
- 英语片“量大不优”:占 56% 数量,但平均分低于意日韩西语。
- 2010 年代产量爆发:270 部上榜,但各年代平均分稳定在 7.89–8.00。
五、第四阶段:Tableau 可视化 + 词云分析
5.1 Tableau 看板设计
建立 2 个仪表板,共 6 张核心图表:
| 图表 | 类型 | 展示内容 |
|---|---|---|
| KPI 卡片 | 文本 | 总数 1000、平均分 7.94、平均时长 115、年份跨度 |
| 评分分布 | 直方图 | 集中于 7.5–8.5 |
| 时长 vs 评分 | 散点图 | 正相关趋势 |
| 年代趋势 | 双轴图 | 柱状产量 + 折线平均分 |
| 语言差异 | 条形图 | 意日韩西语高于英语 |
| 导演四象限 | 散点图 | 大师/劳模/偏科生 |


5.2 词云分析(Python)
使用 jieba + wordcloud 对 1000 条电影简介进行分词与词频统计,生成 5 张词云图:

| 分类 | 高频词 |
|---|---|
| 超高分(>=8.5) | 教父、家族、神秘、希望、黑手党、战争 |
| 中高分(7.5–8.0) | 生活、世界、父亲、妻子、母亲、家庭 |
结论:超高分电影偏向“宏大叙事与强冲突”,中高分电影偏向“日常题材与家庭关系”。
5.3 核心可视化结论
- 超高分电影平均时长 143 分钟,被史诗题材拉高;
- 2020 年代高分片时长回落至 112 分钟,趋向紧凑叙事;
- 英语片数量占优但平均分低于意日韩西语,存在“幸存者偏差”。
六、深入数据分析:三大主题与三个追问
在完成初步的 SQL 筛选和 Tableau 可视化后,我们对数据进行了更深入的主题式分析,形成三个核心主题,并针对每个主题提出了一个关键追问。
主题一:高分电影特征
结论:
- 时长是高分片的硬指标。 超高分电影(≥8.5)平均时长 143 分钟,比中高评分片(7.5–8.0)的 112 分钟整整长了 31 分钟。在 Top-Rated 榜单里,想要冲上 8.5 分,电影几乎都在 2 小时以上(通常是史诗、传记或深度剧情片)。
- 年代不是决定性因素。 虽然超高分片平均年份(2002)比中高片(1997)晚,但差距只有 5 年。说明“老片天然评分高”的假设在 Top-Rated 里不成立,榜单对各个年代的优秀电影是公平的。
注意:超高分电影只有 30 部(占比约 3%),样本量偏小,结论需注明“基于 Top-Rated 榜单内的观察”。
主题二:年代趋势(产量与质量的背离)
核心结论:
- 产量爆发在 2010 年代。 2010 年代上榜电影 270 部,是 2000 年代(145 部)的近 2 倍。但 2020 年代至今(170 部)趋势依然强劲。
- 质量整体非常稳定。 所有年代的平均分都在 7.89–8.00 之间波动,没有出现“越老越经典”或“越新越水”的明显趋势。1990 年代以 8.00 微微领跑,2000 年代略低(7.90)。
- 时长呈抛物线。 电影从 1920 年代的 84 分钟,一路增加到 1970 年代的 133 分钟,然后又回落到 2010 年代的 111 分钟。现代电影越来越短,但产量越来越高。
主题三:语言差异
结论:
- 英语片“量大不优”。 英语片以 560 部的绝对数量占据榜单半壁江山,但平均分(7.94)和高分比例(40.0%)低于日语、西班牙语、朝鲜语和意大利语。
- 意日韩西表现亮眼。 意大利语高分比例最高(52.7%),日语平均分最高(7.99),西班牙语和朝鲜语紧随其后。非英语片在 Top-Rated 里的质量密度更高。
- 法语德语意外偏低。 法语片虽然数量多(62 部),但平均分只有 7.88,高分比例只有 24.2%,低于英语片。这打破了“法语片=文艺高分”的刻板印象。
深入追问一:为什么高评分电影的平均时长在 143 分钟?
1. 类型决定论:143 分钟是被“史诗题材”拉高的
从类型分布看:
- 拉高平均值的类型:剧情(150 分钟)、犯罪(156 分钟)、动作(150 分钟)、奇幻(148 分钟)、冒险(158 分钟)。这些类型往往需要宏大的世界观或复杂的人物弧光(如《指环王》《教父》)。
- 拉低平均值的类型:动画(111 分钟)、爱情(120 分钟)、家庭(114 分钟)、音乐(116 分钟)、纪录(95 分钟)。这些类型更注重情感浓度或创意密度,不需要长篇幅。
2. 年代决定论:2020 年代的“叙事紧凑化”
从年代分布看:
- 1990 年代(165 分钟)、2000 年代(180 分钟)的超高分片极长。
- 但 2020 年代,超高分电影数量最多(10 部),平均时长急剧下降到了 112 分钟。
这意味着:现代电影不再靠“拉长时长”来拿高分,而是靠高密度的信息量和紧凑的叙事。143 分钟是被 70–90 年代的历史/史诗片拉高的,现代高分片其实更短。
3. 个案印证:Top 10 超长高分片全是“史诗”
看看前 10 名:《指环王3》(263 分钟)、《七武士》(207 分钟)、《教父2》(200 分钟)、《辛德勒的名单》(195 分钟)。这些无一例外都是史诗、历史、黑帮题材。它们用超长时长讲透了复杂的时代背景。
最终结论:143 分钟是“史诗题材”的典型特征,而非高分的通用公式。
数据揭示了一个有趣的分化:剧情、犯罪、奇幻等史诗题材的平均时长高达 148–158 分钟,它们拉高了整体均值。但值得注意的是,2020 年代超高分电影的平均时长已大幅回落至 112 分钟,且数量创下新高。这说明现代高分电影不再依赖《指环王》式的超长叙事,而是通过高密度的紧凑叙事取胜。因此,真正的规律是:史诗片需要时长来铺陈,而当代佳作倾向于用更短的篇幅实现高情感密度。
深入追问二:导演的高产与高质能否兼得?
结论:高产与高质并非天然矛盾,但“持续稳定”比“偶尔爆发”更难。
在 TMDb Top 1000 榜单中,诺兰、宫崎骏等导演稳居“大师象限”(作品数 ≥3,平均分 ≥8.0),且他们的评分标准差极小(通常 <0.3),说明他们不仅高产,而且质量极其稳定。
与之相对的是“劳模象限”(作品数多,平均分低于榜单均值),这类导演靠数量上榜,但缺乏顶尖代表作。
值得注意的是,大量非英语导演集中在“偏科生象限”(上榜 1–2 部,平均分极高),这再次印证了非英语片进入 Top 1000 的“幸存者偏差”——只有极其优秀的作品才能突破语言壁垒进入榜单。
此外,从作品平均时长来看,“大师象限”的导演往往偏好长片叙事(如诺兰的 160+ 分钟),这解释了为什么高分电影的平均时长会被拉高至 143 分钟。
深入追问三:高分电影的关键词与时代关联
使用 jieba 对 1000 条电影简介进行分词,并通过 wordcloud 生成词云图,我们得到以下发现:
1. 超高分 vs 中高分的词云差异
- 超高分电影(≥8.5):高频词为 教父、家族、神秘、希望、战斗、黑手党、战争、史诗。题材偏向强戏剧冲突与宏大叙事。
- 中高分电影(7.5–8.0):高频词为 生活、世界、父亲、妻子、母亲、孩子、家庭、意外。题材更贴近日常化与个人命运。
结论:在 TMDb 高分区,探讨家族羁绊、生存希望与正义抗争的宏大题材更容易获得极高评价;而日常题材虽能进入榜单,但很难冲破 8.5 分。
2. 时代演变:从宏大史诗到微观人性
按年代切片后,我们发现:
- 1990 年前:高频词多为 战争、历史、英雄、国家,高分片偏向宏大史诗。
- 1990–2009 年:高频词开始出现 家庭、梦想、自由、爱情,叙事转向个人命运。
- 2010 年后:高频词进一步微观化,出现 现实、成长、孤独、记忆,现代高分片更关注个体的内心世界与精神困境。
这一演变与“电影时长变短”(2010 年代平均 111 分钟)的趋势相呼应。现代高分电影不再依赖超长篇幅去铺陈战争与历史的宏大背景,而是用更短的时长、更紧凑的叙事,去聚焦个体在现实中的情感挣扎。这就是 TMDb 榜单背后的审美变迁。
七、第五阶段:爬虫进阶与数据源切换
7.1 尝试添加评分人数和票房字段
为了让机器学习模型拥有更强的特征,我尝试在爬虫中添加 vote_count(评分人数)和 revenue(票房)字段。
遇到的问题:
- 评分人数抓不到:
rating_details_window是 Kendo UI 动态弹窗,requests拿到的静态 HTML 中不存在该元素,多方案兜底(data-count属性、文本正则、全文搜索)均返回 0。 - API 访问失败:注册 TMDb 官方 API 后,
api.themoviedb.org在国内被墙,连接超时。 - Cloudflare Workers 代理失败:
workers.dev域名同样被墙,WinError 10061目标计算机积极拒绝。 - IP 被限流:频繁请求触发 TMDb 反爬,持续 403 Forbidden。
7.2 解决方案:使用 Kaggle 公开数据集
经过评估,决定放弃继续攻克爬虫,转而使用 Kaggle 公开数据集 TMDB + IMDB Merged Movies Dataset。
数据集信息:
- 68 万条电影记录
- 字段完整:
vote_average、vote_count、revenue、runtime、budget、genres、original_language、directors等 29 个字段 - 下载地址:https://www.kaggle.com/datasets/ggtejas/tmdb-imdb-merged-movies-dataset
八、第六阶段:机器学习预测
8.1 数据准备
- 原始数据:683,475 条
- 清洗后:257,643 条(剔除缺失年份/时长/评分)
- 过滤
vote_count >= 50(保证评分稳定性):27,768 条
8.2 目标变量构造
df['is_high_rated'] = (df['rating'] >= 8.0).astype(int)
结果:高分 602 条,非高分 27,166 条,比例 1:45(严重不平衡)。
8.3 特征工程
- 数值特征:
runtime_min、year、revenue、vote_count - 类别特征:
language(原始语言)、genres(类型)
使用 ColumnTransformer + OneHotEncoder 处理类别变量,RandomForestClassifier 训练模型。
8.4 模型评估结果
========== 模型评估报告 ==========
precision recall f1-score support
非高分 0.99 0.92 0.95 5434
高分 0.11 0.48 0.19 120
accuracy 0.91 5554
macro avg 0.55 0.70 0.57 5554
weighted avg 0.97 0.91 0.93 5554
ROC-AUC 分数:0.8715
核心指标解读:
| 指标 | 值 | 含义 |
|---|---|---|
| ROC-AUC | 0.8715 | 排序能力优秀,87% 概率把高分排前面 |
| macro avg F1 | 0.57 | 高分小类的综合表现 |
| 高分 recall | 0.48 | 抓到约一半的高分电影 |
| accuracy | 0.91 | 整体准确率(但会骗人,因数据不平衡) |
8.5 特征重要性分析
| 排名 | 特征 | 重要性 | 业务含义 |
|---|---|---|---|
| 1 | runtime_min |
23.6% | 时长是最强预测因子 |
| 2 | vote_count |
21.2% | 评分人数次之,高热度=高口碑 |
| 3 | year |
14.0% | 年份有中等影响 |
| 4 | revenue |
7.5% | 票房影响有限,高票房≠高口碑 |
| 5–15 | language_* / genres_* |
各 <5% | 语言和类型影响分散 |
8.6 核心结论
- 时长和评分人数是决定电影高分的最强信号,合计贡献 45% 的重要性,完美验证了 SQL 分析阶段“超高分电影平均 143 分钟”的结论。
- 票房影响有限,说明“商业成功”和“艺术评价”是两回事。
- 语言和类型的影响分散,说明高分电影不存在单一的“类型偏好”。
8.7 模型局限性
- 数据严重不平衡(高分仅占 2.2%),导致高分类别 precision 偏低;
- 未使用 SMOTE 过采样,可通过
imbalanced-learn提升召回率; - 未纳入导演、演员、制作公司等特征。
九、项目成果与交付物
9.1 交付清单
| 交付物 | 说明 |
|---|---|
| Tableau Public 看板 | 6 张图表 + 2 个仪表板([链接]) |
| GitHub 代码仓库 | 爬虫、清洗、SQL、词云、机器学习全流程 |
| PDF 报告 | 本报告,15–20 页 |
| 原始数据集 | 1000 条 TMDb 数据 + Kaggle 68 万条数据 |
| 词云图片 | 5 张(超高分、中高分、1990前、1990–2009、2010后) |
| 特征重要性图 | 1 张 |
9.2 核心结论汇总
- 高分电影特征:超高分电影平均时长 143 分钟,被史诗题材拉高;2020 年代回落至 112 分钟,趋向紧凑叙事。
- 年代趋势:2010 年代产量爆发(270 部),但各年代平均分稳定在 7.89–8.00。
- 语言差异:英语片数量占优(56%),但平均分(7.94)低于意日韩西语,存在“幸存者偏差”。
- 文本挖掘:超高分电影高频词为“家族、希望、战斗”,中高分电影为“生活、父亲、妻子”。
- 机器学习:ROC-AUC 0.8715,“时长”和“评分人数”是最强预测因子。
十、技术难点与解决过程
| 难点 | 解决方案 |
|---|---|
| 评分人数在静态 HTML 中缺失 | 多 XPath 兜底 + 转向 Kaggle 数据集 |
api.themoviedb.org 被墙 |
尝试 Cloudflare Workers 代理,最终改用公开数据集 |
| IP 被 TMDb 限流 | 随机延迟、Session 复用、自动重试 |
| 数据严重不平衡 | 使用 class_weight='balanced' 缓解 |
| 中文在 matplotlib 中乱码 | 设置 plt.rcParams['font.sans-serif'] |
项目最大收获:数据获取不是目的,分析结论才是。当爬虫遇到不可逾越的障碍时,及时切换数据源(Kaggle 公开数据集)是专业数据分析师的选择,而非在技术细节上死磕。
十一、局限性与未来改进
- 幸存者偏差:TMDb Top-Rated 榜单只有高分电影,缺少低分对照组,无法得出“电影越长评分越高”的普适结论。
- 特征不足:未纳入导演、演员、制作公司、预算等特征,模型可以进一步优化。
- 数据不平衡:高分样本仅 2.2%,可通过 SMOTE 过采样或调整阈值提升 recall。
- 文本挖掘深度:仅做词频统计,未来可尝试 TF-IDF、LDA 主题模型或 BERT 情感分析。
浙公网安备 33010602011771号