用Python实现影视预告评论情感分析与可视化实战
最近《米尔扎布尔》(Mirzapur)电影版正式预告发布的消息,让很多追剧人瞬间来了精神。作为印度 Amazon Prime Video 上最具辨识度的犯罪剧集之一,这部剧凭借硬核的暴力美学、家族权力斗争和密集的剧情反转,积累了大量忠实观众。不过在 CSDN 上,除了聊预告本身,我们其实还可以换一个角度:怎么把一个影视热点变成一次有意思的数据分析实战。
这篇文章不打算做影评,而是围绕“影视预告热度分析”这个方向,完整演示一套基于 Python 的数据处理流程。即使你完全不了解《米尔扎布尔》,也不影响阅读。核心内容包括:数据从哪来、怎么清洗、情感分析怎么做、结果如何可视化,以及在实际操作中最容易踩的坑。项目代码可以直接复制运行,适合想通过真实场景练手的数据分析初学者,也适合正在做影视内容相关产品、需要设计热度监控方案的开发同学。
1. 从预告发布聊起:影视热度分析到底在分析什么
1.1 一部剧的预告片,能带来哪些可量化数据
预告片发布本质上是一次内容营销事件。围绕这个事件,会产生非常多的数据,而且每一类数据的分析价值都不一样。
第一类是播放数据。预告片在 YouTube、Prime Video 等平台上的播放量、完播率、平均观看时长,能直接反映观众对预告内容的接受程度。播放量高但完播率低,说明标题和封面足够吸引人,但正片内容没能留住观众,这往往意味着预告片剪辑节奏或核心看点呈现出了问题。
第二类是互动数据。点赞数、点踩数、收藏数、分享数、评论数,这些指标比播放数量更能反映观众的真实情绪。尤其是指踩与评论区的措辞,很多时候比平台内置的评分更早暴露口碑趋势。
第三类是舆情文本数据。评论区、社交平台讨论帖、新闻稿标题,这些都是非结构化文本。它们蕴藏着观众最关心的信息点,比如某个角色的回归、剧情尺度、拍摄质感、与原剧的延续性等等。用自然语言处理技术对这些文本做情感分析,就能得到一张“观众整体情绪随时间变化”的曲线。
所以,影视热度分析并不是一个简单的“读播放量”的动作,而是一套从数据采集、清洗、建模到可视化的完整数据工程流程。本文后续讲解,全部围绕第三条线——评论文本分析展开,这也是数据量最大、最值得做的部分。
1.2 为什么适合用 Python 来做
Python 在数据处理领域有天然优势。pandas 提供了强大的 DataFrame 结构,可以快速完成数据清洗、筛选、聚合;jieba、SnowNLP 等库能处理文本分词和基础情感判断;wordcloud 能把高频词变成词云图;matplotlib、pyecharts 能输出用于汇报展示的图表。
更关键的是,Python 的代码表达能力很强。一个 200 行之内的脚本,就能完成从原始评论到可视化图表的全链路处理。这对于个人开发者、产品运营和数据小白来说,学习成本非常低。而且这套流程并不只适用于《米尔扎布尔》预告片,你可以把它平移到任何一个影视作品、产品发布会、体育赛事热点上,属于非常通用的分析能力。
2. 环境准备与项目结构
2.1 运行环境与依赖版本
本文示例代码以 Python 3.9+ 为基础,建议使用独立的虚拟环境,避免污染系统 Python。操作系统方面,Windows、macOS、Linux 都支持。由于涉及中文字体显示,Windows 环境下使用系统自带的微软雅黑即可,macOS 可以使用系统中文字体。
需要安装的依赖库如下:
pip install pandas==2.0.3 pip install matplotlib==3.7.2 pip install wordcloud==1.9.2 pip install jieba==0.42.1版本不强制锁定,如果你已经安装了更新版本,通常也能正常运行。如果安装wordcloud时遇到编译问题,在 Windows 上建议直接下载对应 Python 版本的 whl 文件安装,或者使用 Anaconda 环境来规避编译依赖。
2.2 项目目录结构
为了便于理解和后续扩展,我们按照下面的目录结构组织项目:
mirzapur_analysis/ ├── data/ │ ├── raw/ │ │ └── comments.json │ └── cleaned/ │ └── comments_clean.csv ├── output/ │ ├── sentiment_pie.png │ ├── trend_line.png │ └── wordcloud.png ├── src/ │ ├── clean.py │ ├── sentiment.py │ └── visualize.py └── requirements.txtdata/raw存放原始评论文本,格式为 JSON;data/cleaned存放清洗后的结构化数据;output保存可视化图片;src下按职责拆分代码文件。
在实际项目中,原始数据一般来自评论接口或爬虫,这里为了演示,我们自己构造一份包含中英文评论的 JSON 数据。模拟数据能让你更专注地理解处理思路,后续接入真实数据时,只需要替换数据读取部分即可。
3. 数据的合法获取与字段设计
3.1 评论数据从哪里来
很多同学拿到一个分析需求,第一反应就是“写爬虫去抓”。但对于影视评论来说,优先考虑的应该是官方接口。
YouTube 对每条视频提供了 Data API v3,通过commentThreads.list可以获取视频下的评论列表。Amazon Prime Video 本身没有公开的评论 API,不过在公开讨论区、IMDb 评论区也有大量内容。IMDb 提供了非商用数据集,但评论明细的实时获取还是需要遵守其服务条款。
这里的核心原则是:优先使用官方渠道,注意 API 调用频率限制,不要对目标平台造成访问压力。如果是商业项目,务必确认数据使用是否合规,避免因抓取行为带来法律风险。
3.2 数据字段设计
无论从哪个渠道拿数据,最终我们都需要把数据结构化。建议至少保留以下字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| comment_id | string | 评论唯一ID |
| user_name | string | 用户名,脱敏处理 |
| text | string | 评论文本 |
| published_at | datetime | 发布时间 |
| like_count | int | 点赞数 |
| reply_count | int | 回复数 |
| source | string | 数据来源平台 |
comment_id用于去重;published_at用于后续的时间趋势分析;like_count可以作为情感权重的参考——一个高赞的负面评论,和一条无人问津的负面评论,对整体口碑的影响完全不同。
本文演示用的模拟数据就是按照这个结构设计的。
3.3 构建一份可用的模拟数据集
在data/raw/comments.json中写入如下内容:
[ {"comment_id": "001", "user_name": "user_01", "text": "Great trailer, can't wait for the movie!", "published_at": "2024-05-20 08:05:00", "like_count": 128, "reply_count": 12, "source": "youtube"}, {"comment_id": "002", "user_name": "user_02", "text": "终于等到电影版了,预告片节奏很燃,期待上映!", "published_at": "2024-05-20 08:12:00", "like_count": 256, "reply_count": 23, "source": "prime"}, {"comment_id": "003", "user_name": "user_03", "text": "Boring, nothing special about this teaser.", "published_at": "2024-05-20 08:20:00", "like_count": 3, "reply_count": 0, "source": "youtube"}, {"comment_id": "004", "user_name": "user_04", "text": "豆瓣肯定会炸,这尺度看着比剧版还猛。", "published_at": "2024-05-20 08:35:00", "like_count": 189, "reply_count": 16, "source": "douban"}, {"comment_id": "005", "user_name": "user_05", "text": "没有任何一个角色是安全的,这才是 Mirzapur 的灵魂", "published_at": "2024-05-20 08:44:00", "like_count": 357, "reply_count": 30, "source": "twitter"}, {"comment_id": "006", "user_name": "user_06", "text": "节奏真快,两分钟的预告信息量拉满", "published_at": "2024-05-20 09:02:00", "like_count": 96, "reply_count": 5, "source": "bilibili"}, {"comment_id": "007", "user_name": "user_07", "text": "Again a glorification of violence, not interested.", "published_at": "2024-05-20 09:15:00", "like_count": 21, "reply_count": 4, "source": "youtube"}, {"comment_id": "008", "user_name": "user_08", "text": "如果剧情像第三季一样拖沓,电影版也救不回来", "published_at": "2024-05-20 09:30:00", "like_count": 45, "reply_count": 9, "source": "weibo"} ]这份数据包含了英语、中文混合评论,覆盖正面、负面、中性不同情感倾向。由于是模拟数据,字段结构完全由我们自己掌控,你可以随时追加更多评论,来观察分析结果的变化。
4. 核心实现:从清洗到情感分析
4.1 数据读取与基础清洗
原始评论不能直接用于分析,因为里面夹杂着大小写差异、表情符号、URL、@提及等噪声。清洗的目的就是把文本整理成“干净、可计算”的格式。
在src/clean.py中写入以下代码:
import json import re import pandas as pd def load_data(file_path): with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) df = pd.DataFrame(data) df["published_at"] = pd.to_datetime(df["published_at"]) return df def clean_text(text): # 转小写,便于统一英文大小写 text = text.lower() # 移除 URL text = re.sub(r"http\S+|www\.\S+", "", text) # 移除 @ 提及 text = re.sub(r"@\w+", "", text) # 移除多余空白字符 text = re.sub(r"\s+", " ", text).strip() return text def clean_data(df): df["clean_text"] = df["text"].apply(clean_text) # 去掉空文本 df = df[df["clean_text"] != ""] # 按评论ID去重,保留第一条 df = df.drop_duplicates(subset="comment_id", keep="first") return df if __name__ == "__main__": df = load_data("../data/raw/comments.json") df = clean_data(df) df.to_csv("../data/cleaned/comments_clean.csv", index=False, encoding="utf-8-sig") print(df.head())清洗公式中有几个值得注意的细节。
pd.to_datetime会把字符串时间统一转换为 pandas 的 datetime 类型,方便后续按小时、日、周聚合。re.sub(r"http\S+|www\.\S+", "", text)使用正则匹配并移除 URL,避免链接干扰分词和情感判断。utf-8-sig编码是为了让 CSV 文件在 Excel 中打开时中文不乱码。
第一次运行前检查一下路径,确保当前工作目录在src/下,或者使用绝对路径。控制台如果输出前五行数据,说明清洗流程已经跑通。
4.2 在清洗基础上增加字段
清洗后的数据只能算“机器可读”,还称不上“分析友好”。在实际项目中,我们通常还会增加两个标准字段:
df["length"] = df["clean_text"].str.len() df["word_count"] = df["clean_text"].str.split().apply(len)length表示评论字符数,word_count表示分词后的词数。这两个指标可以帮我们发现异常评论。例如 500 字以上的超长评论,通常是粉丝在写详细观后感,系统崩溃式的高频刷屏文本,也可能是机器产生的垃圾评论。
更严谨的做法是建立评论质量规则,比如“命中固定广告词列表的评论直接过滤”“连续重复字符超过 5 个的评论打标”。这些规则可以沉淀成独立的filter_rules.py文件,而不是全部堆在清洗脚本里,便于后期维护和更新。
4.3 情感分析的轻量实现方案
提到情感分析,很多同学第一反应是用机器学习或者大模型。但对于评论数据量在几百到几万条的场景,基于情感词典的轻量方案完全够用,而且部署成本极低、运行速度快、结果可解释性强。
核心思路是维护两个词表:积极词表和消极词表。对每条评论,统计命中积极词和消极词的次数,再用公式计算情感得分:
sentiment_score = (positive_num - negative_num) / (positive_num + negative_num + 1)分母加 1 是为了防止除零。得分范围落在 -1 到 1 之间:大于 0 判定为正面,小于 0 判定为负面,等于 0 判定为中性。
在src/sentiment.py中写入:
import pandas as pd POSITIVE_WORDS = { "great", "amazing", "excellent", "good", "awesome", "love", "期待", "燃", "精彩", "震撼", "喜欢", "经典", "不错", "拉满", "灵魂" } NEGATIVE_WORDS = { "bad", "boring", "terrible", "awful", "worst", "hate", "拖沓", "失望", "垃圾", "无聊", "救不回来" } def get_sentiment_scores(text): words = text.split() pos_count = sum(1 for w in words if w in POSITIVE_WORDS) neg_count = sum(1 for w in words if w in NEGATIVE_WORDS) score = (pos_count - neg_count) / (pos_count + neg_count + 1) return pos_count, neg_count, round(score, 4) def label_sentiment(score): if score > 0: return "正面" elif score < 0: return "负面" return "中性" def analyze_sentiment(df): df[["pos_num", "neg_num", "sentiment_score"]] = df["clean_text"].apply( lambda x: pd.Series(get_sentiment_scores(x)) ) df["sentiment_label"] = df["sentiment_score"].apply(label_sentiment) return df此处对中文使用了split()按空格分割。但中文文本没有天然空格,split()会把整句当作一个词,导致词典匹配失效。所以中文评论必须经过分词处理。
在项目里加上分词增强逻辑:
import jieba def tokenize_text(text): seg_list = jieba.lcut(text) return " ".join(seg_list)清洗之后、情感分析之前,先对中文文本分词,再重新拼接成以空格分隔的文本。这样后续split()得到的每个词才有意义。
由于本文模拟数据中既有英文又有中文,代码里直接对clean_text做分词会出现中英混切的问题。为了演示效果更稳定,建议先用一个简单规则判断文本是否包含中文,再决定是否走 jieba 分词:
import re def is_chinese(text): return bool(re.search(r"[\u4e00-\u9fa5]", text)) def smart_tokenize(text): if is_chinese(text): return " ".join(jieba.lcut(text)) return text.lower()这种中文英文分开处理的策略,在真实评论场景中非常实用。因为评论区通常是多语言混合的,统一走英文分词会丢失中文语义,统一走中文分词又会把英文单词拆坏。
4.4 完整的主流程串联
在src/main.py中把所有步骤串起来:
import pandas as pd from clean import load_data, clean_data, clean_text from sentiment import analyze_sentiment def main(): df = load_data("../data/raw/comments.json") df = clean_data(df) df["clean_text"] = df["clean_text"].apply(smart_tokenize) df = analyze_sentiment(df) print(df[["comment_id", "clean_text", "sentiment_score", "sentiment_label"]]) df.to_csv("../data/cleaned/comments_sentiment.csv", index=False, encoding="utf-8-sig") if __name__ == "__main__": main()运行后控制台会输出如下格式的结果:
comment_id clean_text sentiment_score sentiment_label 0 001 great trailer , can ' t wait for the movie ! 0.5000 正面 1 002 终于 等到 电影版 了 , 预告片 节奏 很 燃 , 期待 上映 ! 0.4000 正面值得注意的是,第一条英文评论在分词后出现了can ' t这种被拆开的形态,这是因为我们统一使用空格分词,没有做英文词形还原。对于情感词典方案,can't被拆开并不会影响主要内容词的匹配,所以可以接受。
5. 可视化和趋势解读
5.1 情感分布饼图
数据算出情感标签后,第一步通常是看整体分布。写一个src/visualize.py:
import pandas as pd import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = False def draw_sentiment_pie(df): labels = df["sentiment_label"].value_counts().index.tolist() counts = df["sentiment_label"].value_counts().tolist() plt.figure(figsize=(8, 6)) plt.pie(counts, labels=labels, autopct="%.1f%%", startangle=90) plt.title("《米尔扎布尔》电影版预告观众评论情感分布") plt.savefig("../output/sentiment_pie.png", dpi=150, bbox_inches="tight") plt.close() if __name__ == "__main__": df = pd.read_csv("../data/cleaned/comments_sentiment.csv") draw_sentiment_pie(df)这里的rcParams设置非常关键。如果不设置中文字体,Matplotlib 默认字体无法显示中文,图内文字会变成一堆方框。在 Linux 服务器上更是需要提前确认系统中是否安装了中文字体,否则必须切换字体或改用英文标签。
5.2 评论时间趋势图
趋势图能看到评论热度是脉冲式的还是长尾式的。预告片刚发布时评论通常集中爆发,随后快速下降,这叫脉冲式热度。长尾式则说明作品有持续性讨论,通常是口碑发酵的表现。
def draw_trend_line(df): df["date"] = df["published_at"].dt.date daily_count = df.groupby("date").size().reset_index(name="comment_num") plt.figure(figsize=(12, 5)) plt.plot(daily_count["date"], daily_count["comment_num"], marker="o") plt.title("评论数量随时间变化") plt.xlabel("日期") plt.ylabel("评论数") plt.grid(True, linestyle="--", alpha=0.6) plt.xticks(rotation=45) plt.tight_layout() plt.savefig("../output/trend_line.png", dpi=150) plt.close()视觉化的价值在于让结论一目了然。如果数据源是真实接口,published_at是 UTC 时间,画图前还需要统一转成东八区时间,否则按天聚合会错位。
5.3 词云图制作
词云是另一个非常适合做“快速抓重点”的可视化形式。高频出现的角色名、场景词、评价词,能直观反映观众讨论焦点。
from wordcloud import WordCloud def draw_wordcloud(df): text = " ".join(df["clean_text"].tolist()) wc = WordCloud( font_path="C:/Windows/Fonts/msyh.ttc", width=1200, height=700, background_color="white", colormap="viridis", max_words=100, collocations=False ).generate(text) wc.to_file("../output/wordcloud.png")font_path参数是中文词云的必选项。Windows 下使用微软雅黑路径C:/Windows/Fonts/msyh.ttc;macOS 下使用System/Library/Fonts/PingFang.ttc;Linux 如果没有中文字体,则需要先安装fonts-wqy-zenhei之类的字体包。
当前这份只有 8 条评论的模拟数据,词云效果不会太丰富。你可以往comments.json里追加几十条评论,会发现词云里的高频词逐渐稳定下来,变得越来越有分析价值。
6. 常见报错与排查思路
实测过程中,最容易遇到下面几类问题。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
pandas 读取 JSON 报错ValueError: Expected object | JSON 文件编码或格式异常 | 检查文件是否为 UTF-8 编码,用 JSON 校验工具格式化 |
| 中文显示为方框 | Matplotlib 未配置中文字体 | 设置rcParams["font.sans-serif"],确认系统有中文字体 |
wordcloud安装失败 | 缺少编译环境 | Windows 下下载 whl 包离线安装,或使用 Anaconda |
| 评论时间全部变成 NaT | 时间格式不统一 | 使用pd.to_datetime的format参数统一格式 |
| 情感得分全为 0 | 中文未分词导致词典失效 | 对中文文本先使用 jieba 分词再匹配词表 |
| 词云出现大量无意义单字 | collocations=False未设置 | wordcloud 2.0 以上版本默认开启二元组,建议关闭 |
如果情感分析结果看起来与直觉不符,先不要急着怀疑模型,优先检查词典覆盖度。词典方案的天花板由词表质量决定,词表里没有“绝了”“封神”“烂尾”这些高频网络词,结果自然会有偏差。
生产环境中更推荐使用基于预训练模型的情感分类服务,或者调用大模型 API 做结构化输出。词典方案的定位是快速验证、离线可跑、成本为零,适合给中小规模数据做初筛。
7. 工程化落地建议
7.1 把这套流程变成定时任务
在开发环境跑通之后,如果要持续跟踪《米尔扎布尔》电影版后续的宣传节奏,可以考虑把脚本改造成定时任务。Linux 下使用 crontab,Windows 下使用计划任务。每次调度执行后,把最新图片推送到内部群或报表页面,就能形成一套完整的监控机制。
更合理的数据流设计是:
采集 → 原始数据存储 → 清洗 → 入库 → 分析 → 可视化 → 输出报表每一层独立存放。清洗前的数据永远不修改,清洗后的结果写入单独的表或文件,方便出了问题追溯源头。
7.2 注意异常处理和日志
脚本化运行之后,异常处理就不能只靠 print 了。至少在关键步骤加上 try-except 和日志输出。
import logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") def main(): try: df = load_data("../data/raw/comments.json") logging.info("数据加载成功,共 %s 条", len(df)) except FileNotFoundError: logging.error("原始数据文件不存在,请检查路径") return日志里记录“数据条数”“过滤条数”“正面/负面占比”这些关键指标,有助于快速判断当次运行是否正常。比如某次运行突然发现评论数比前一天少了 90%,很可能是接口返回异常或过滤规则误伤,而不是热度真的下降了。
7.3 安全与合规边界
影视评论数据属于平台用户生成内容,使用时需要注意几点。
不要存储不必要的个人信息。用户名和用户 ID 只保留分析所需的最小字段,能脱敏就脱敏。不要将评论数据用于商业模型训练或对外分发,除非确认数据授权范围。不要高频率请求接口,更不要绕过平台的风控策略。API 调用需要遵守平台速率限制,建议在代码中增加随机延时或令牌桶限流。
7.4 后续可以扩展的方向
如果想把这套项目继续做深,有四个方向可以参考。
一是加入更细粒度的主题分类。不只判断情感,还判断观众在讨论角色、剧情、画面、音乐还是演员,需要构建一个简单的关键词规则分类器。
二是引入热度指数计算。将播放量、点赞数、评论数、分享数多个维度加权,形成一个综合热度分,避免单一指标带来的误判。
三是对接大模型做摘要。每天把新增评论喂给大模型,输出“今日观众关注点”的摘要,甚至可以自动生成报告,减少人工整理成本。
四是前端可视化。用 Flask 或者 FastAPI 把分析结果封装成 HTTP 接口,再配合 ECharts 做成一个简单的数据看板,这样团队其他人也能直接访问。
8. 练习建议与最后的提醒
如果你完全跟着本文代码做了一遍,现在应该已经掌握了一套完整的评论数据处理链路:从 JSON 加载、文本清洗、中英文分词、情感打分到图表输出。这套技能可以复用到很多场景,不限于影视评论,像应用商店用户评价、公众号留言、电商商品评论,思路完全一致。
建议你替换成自己关注的作品,找一份真实的公开数据集,把这份代码跑通。真实数据和模拟数据最大的不同在于噪声更多——长评论、表情符号、网络用语、错别字,每一项都会考验清洗规则。能正确处理真实数据,才算是把这套流程真正变成自己的东西。
如果你正在关注《米尔扎布尔》电影版的后续动态,不妨把这些分析代码跑起来,等预告片正式放出后再把真实评论填进去。你可能会看到播放量、评论情绪和口碑走势之间的关系,远比想象中更有意思。遇到具体报错,欢迎在评论区留言交流。
