简介:这是一套面向计算机相关专业毕设学生与Python实战学习者的开心麻花影视作品分析系统,围绕《西虹市首富》《夏洛特烦恼》等影片展开数据采集与可视化分析。资源包共33个文件,约158MB,包含Python源码、可执行程序、配置说明书与使用说明书,并配有html可视化页面、json城市坐标数据、xlsx影评数据、ttf字体、stopwords停用词表及ui界面文件等,覆盖从数据抓取到图表呈现的完整链路。系统支持选择电影后分析评论数、平均分,生成评论词云图与全国评论分布热力图,帮助读者理解urllib请求、collections统计、json解析与ECharts可视化的综合运用。目前已有251人学习,适合作为毕设参考或项目实战练习,可据此掌握数据采集、清洗、统计与前端展示的完整开发思路。
1. 从零拆解:开心麻花影视作品分析系统到底在分析什么
很多人第一次看到「开心麻花影视作品分析系统」这个标题,脑子里冒出来的第一个问题是:这不就是个爬虫加图表吗?我一开始也这么想,直到真正动手把开心麻花从《夏洛特烦恼》到《独行月球》这一串作品的数据拉齐,才发现事情没那么简单。这个系统的核心不是「爬」,而是「对齐」——把豆瓣评分、票房、上映时间、演员阵容、观众短评这几路异构数据,按作品维度拼成一张能横向对比的表,再从中挖出「哪部片子口碑票房双高」「哪个演员是票房稳定器」「喜剧密度和评分有没有相关性」这类结论。
它适合两类人:一类是刚学完 Python 基础、想找一个有真实数据、有可视化界面、能写进简历的练手项目的人;另一类是做内容运营或影视投资分析、需要一套能自己改指标、自己加作品的轻量分析工具的人。标题里提到的源码、可执行程序、配置说明书、使用说明书,本质上就是把这套东西从「我电脑上能跑」变成「换台机器也能跑」的完整交付物。
这一章先把边界划清楚:系统分析的对象是开心麻花出品的院线电影和部分话剧改编作品,数据来源以公开的影视平台为主,分析维度包括票房、评分、口碑分布、演员贡献度、时间趋势五块。后面几章会从环境搭建、数据采集、分析逻辑、界面打包一路讲到踩坑和进阶玩法,你跟着走完,手里应该能跑起来一个能改、能扩、能交付的版本。
2. 环境搭建与项目骨架:让源码在你机器上先跑起来
2.1 Python 环境与依赖的版本选择
拿到源码包之后,第一件事不是急着python main.py,而是先把环境对齐。这类影视分析系统常见的依赖组合是 Python 3.8 到 3.10 之间,再高容易在 PyInstaller 打包和某些可视化库上翻车。我一般会先用 conda 建一个独立环境,避免和系统里其他项目的包打架。
# 创建独立环境,指定 3.9 这个在打包和可视化上都比较稳的版本 conda create -n mht_analysis python=3.9 -y conda activate mht_analysis # 安装核心依赖,版本号是血泪经验,别随意升 pip install pandas==1.5.3 numpy==1.23.5 pip install requests==2.28.2 beautifulsoup4==4.11.2 pip install matplotlib==3.6.3 pyecharts==1.9.1 pip install pyinstaller==5.7.0这里每个包都有存在的理由:pandas 负责把爬下来的零散数据整理成 DataFrame,requests 和 beautifulsoup4 是采集层的主力,matplotlib 和 pyecharts 一个出静态图一个出交互图,pyinstaller 则是最后把脚本变成 exe 的关键。版本锁死是因为 pyecharts 1.9 和 2.x 的 API 差异很大,网上很多示例代码是 1.x 写法,你装了 2.x 会直接报ImportError。
提示:如果你用的是 vscode python 环境配置,记得在 settings.json 里把
python.defaultInterpreterPath指向刚建的 conda 环境,否则终端里跑通了、调试器里却找不到包,这种玄学问题能查半天。
2.2 项目目录结构与配置文件的读法
源码包解压后,目录一般长这样,我按功能给你拆开看:
mht_analysis/ ├── config/ │ └── settings.ini # 数据库、采集间隔、输出路径 ├── data/ │ ├── raw/ # 原始采集数据 │ └── processed/ # 清洗后的分析用数据 ├── src/ │ ├── collector.py # 采集模块 │ ├── analyzer.py # 分析模块 │ └── visualizer.py # 可视化模块 ├── main.py # 入口 └── requirements.txt配置说明书里最容易被忽略的是settings.ini里的采集间隔和重试次数。默认值往往写得很保守,你如果直接跑,采集几十条短评可能要等好几分钟。我一般会把interval从 2 秒调到 0.5 秒,retry从 3 次调到 5 次,但别调到 0,否则请求太密容易被目标站点限流,这就是典型的「快就是慢」。
[collector] interval = 0.5 retry = 5 timeout = 10 [output] raw_path = ./data/raw processed_path = ./data/processed改完配置先别急着全量跑,用main.py --mode test这种测试模式先拉三五条数据,确认字段能对上、编码不乱码,再放开跑全量。这一步能帮你省下大量「跑了一小时发现字段全错」的后悔药。
3. 数据采集与清洗:把散落的票房和评分拼成一张表
3.1 采集层的请求构造与字段映射
采集模块的核心逻辑是「按作品名去查,把返回的 JSON 或 HTML 解析成统一字段」。不同平台返回结构不一样,所以 collector.py 里通常会有一个字段映射字典,把平台字段名映射成系统内部字段名。下面这段是我常用的写法,你可以直接套:
import requests from bs4 import BeautifulSoup import time # 平台字段 -> 系统内部字段的映射,改这里就能适配新数据源 FIELD_MAP = { "movie_name": "title", "release_date": "release", "box_office": "box", "rating": "score", "comment_count": "comments" } def fetch_movie_detail(movie_id, retry=5, interval=0.5): url = f"https://example.com/api/movie/{movie_id}" for attempt in range(retry): try: resp = requests.get(url, timeout=10, headers={"User-Agent": "Mozilla/5.0"}) if resp.status_code == 200: raw = resp.json() # 按映射表转成内部字段,缺字段给默认值 return {v: raw.get(k, None) for k, v in FIELD_MAP.items()} except requests.RequestException: time.sleep(interval * (attempt + 1)) # 退避重试 return None这段代码的关键在两点:一是FIELD_MAP把平台差异隔离在一处,以后换数据源只改字典不改逻辑;二是重试用了退避策略,第几次失败就等几倍间隔,比固定 sleep 更稳。参数上timeout别设太大,10 秒足够,设 30 秒只会让你在对方挂掉时等更久。
3.2 清洗规则:缺失值、异常票房和重复作品
采集回来的原始数据一定脏。常见脏法有三种:票房字段带「万」「亿」单位、评分出现 0 分这种明显缺失、同一部作品因为不同来源被采了两次。清洗模块要按顺序处理:
| 问题类型 | 判断规则 | 处理方式 |
|---|---|---|
| 单位不统一 | 字段含「万」「亿」 | 统一换算成「万元」数值 |
| 评分缺失 | score 为 0 或空 | 标记为 NaN,分析时剔除 |
| 重复作品 | title 完全相同 | 保留字段最全的一条 |
| 日期格式乱 | 多种日期写法 | 统一转成 YYYY-MM-DD |
import pandas as pd import numpy as np def clean_box_office(value): if pd.isna(value): return np.nan value = str(value) if "亿" in value: return float(value.replace("亿", "")) * 10000 if "万" in value: return float(value.replace("万", "")) return float(value) df = pd.read_csv("./data/raw/movies.csv") df["box"] = df["box"].apply(clean_box_office) df["score"] = df["score"].replace(0, np.nan) df = df.sort_values("comments", ascending=False).drop_duplicates("title") df.to_csv("./data/processed/movies_clean.csv", index=False)清洗顺序不能乱:先统一单位再做数值计算,先去重再统计,否则你算出来的平均票房会被重复作品拉偏。这一步做完,你手里才有一张能拿来做分析的干净表。
4. 分析逻辑与可视化:从一张表里挖出可讲的结论
4.1 票房与评分的双维度对比
分析模块最核心的一张图是「票房-评分散点图」,横轴票房、纵轴评分,每个点是一部作品。这张图能一眼看出哪些片子是「叫好又叫座」,哪些是「高票房低口碑」。用 matplotlib 实现:
import matplotlib.pyplot as plt import pandas as pd plt.rcParams["font.sans-serif"] = ["SimHei"] # 中文显示,不加这行全是方块 plt.rcParams["axes.unicode_minus"] = False df = pd.read_csv("./data/processed/movies_clean.csv") fig, ax = plt.subplots(figsize=(10, 6)) ax.scatter(df["box"], df["score"], s=df["comments"] / 1000, alpha=0.6) for _, row in df.iterrows(): ax.annotate(row["title"], (row["box"], row["score"]), fontsize=8) ax.set_xlabel("票房(万元)") ax.set_ylabel("评分") ax.set_title("开心麻花作品票房-评分分布") plt.tight_layout() plt.savefig("./data/processed/scatter.png", dpi=150)font.sans-serif设成 SimHei 是必须的,否则中文标题全是豆腐块,这是新手最常见的翻车点。s参数用评论数控制点的大小,评论越多点越大,相当于把「热度」这个维度也塞进了二维图里。
4.2 演员贡献度的量化思路
「哪个演员是票房稳定器」这个问题,可以用一个简单但有效的指标:某演员参演作品的平均票房,除以全体作品平均票房,得到一个贡献系数。系数大于 1 说明高于平均,小于 1 说明拖后腿。
# 把演员字段按逗号拆开,一行变多行 df["actors"] = df["actors"].str.split(",") exploded = df.explode("actors") exploded["actors"] = exploded["actors"].str.strip() avg_box = df["box"].mean() actor_stat = exploded.groupby("actors").agg( movie_count=("title", "count"), avg_box=("box", "mean"), avg_score=("score", "mean") ).reset_index() actor_stat["contribution"] = actor_stat["avg_box"] / avg_box actor_stat = actor_stat[actor_stat["movie_count"] >= 2] # 只保留参演两部以上的 actor_stat.sort_values("contribution", ascending=False, inplace=True)movie_count >= 2这个过滤条件很重要,只参演过一部的演员样本太小,算出来的系数没有统计意义。这个思路你也可以换成导演、编剧维度,逻辑完全一样。
4.3 用 pyecharts 做可交互的界面图表
静态图适合写报告,但系统要给人用,就得有交互。pyecharts 能生成 HTML,鼠标悬停能看到具体数值,适合嵌进界面。
from pyecharts.charts import Bar from pyecharts import options as opts top10 = actor_stat.head(10) bar = ( Bar() .add_xaxis(top10["actors"].tolist()) .add_yaxis("贡献系数", top10["contribution"].round(2).tolist()) .set_global_opts( title_opts=opts.TitleOpts(title="演员票房贡献系数 Top10"), xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=30)) ) ) bar.render("./data/processed/actor_bar.html")rotate=30是防止演员名字太长挤在一起,这个参数在名字超过四个字时必调。生成的 HTML 可以直接用浏览器打开,也可以嵌进 PyQt 或 Flask 做的界面里。
5. 打包与交付:把脚本变成别人能双击运行的程序
5.1 用 PyInstaller 生成可执行程序
源码交付最大的问题是对方没装 Python。PyInstaller 能把脚本和依赖打成一个 exe,对方双击就能跑。命令不复杂,但参数有讲究:
pyinstaller --name MHTAnalysis \ --onefile \ --add-data "config;config" \ --add-data "data;data" \ --hidden-import pyecharts \ --noconsole \ main.py--onefile打成单文件方便传输,但启动会慢几秒,因为要先解压到临时目录。--add-data把配置和数据目录一起打进去,注意 Windows 下分隔符是分号,Linux 下是冒号,搞错了打包能成功但运行时找不到文件。--hidden-import是因为 pyecharts 有些子模块是动态导入的,不显式声明会被漏掉。--noconsole去掉黑框,但调试阶段建议先不加,留着黑框看报错。
5.2 配置说明书和使用说明书的写法
这两份文档不是凑数的,是交付质量的分水岭。配置说明书要写清楚三件事:环境要求(Python 版本、依赖清单)、配置文件每个字段的含义和取值范围、常见配置错误的排查方法。使用说明书则按用户操作路径写:怎么启动、界面每个按钮干什么、数据从哪来、结果存哪、出错了看哪个日志。
我一般会在说明书里放一张「症状-原因-解决」对照表,比大段文字管用:
| 症状 | 可能原因 | 解决 |
|---|---|---|
| 双击没反应 | 缺 VC++ 运行库 | 装对应版本运行库 |
| 图表中文乱码 | 系统无 SimHei 字体 | 装字体或换字体配置 |
| 采集全失败 | 网络或目标站点变更 | 检查网络,更新采集规则 |
| 打包后找不到配置 | add-data 路径错 | 核对分隔符和相对路径 |
6. 避坑与排查:那些让我熬夜的翻车现场
6.1 编码问题:中文乱码的三个来源
现象:采集回来的短评全是\uXXXX或者问号。原因通常有三个:请求没设resp.encoding、写 CSV 没指定encoding="utf-8-sig"、控制台编码不是 UTF-8。解决方法是请求后手动resp.encoding = "utf-8",写文件统一用utf-8-sig(带 BOM,Excel 打开不乱码),控制台用chcp 65001切到 UTF-8。
6.2 打包后路径错乱
现象:源码里跑得好好的,打包成 exe 后报FileNotFoundError。原因是 PyInstaller 打包后,程序运行时的临时目录和源码目录不一样,所有相对路径都会失效。解决办法是用sys._MEIPASS判断运行环境:
import sys, os def resource_path(relative): if hasattr(sys, "_MEIPASS"): return os.path.join(sys._MEIPASS, relative) return os.path.join(os.path.abspath("."), relative)所有读配置、读数据的地方都走这个函数,就不会再找不到文件。
6.3 采集被限流的信号与应对
现象:跑了几十条之后突然全部返回 403 或空数据。原因是请求频率触发了目标站点的防护。解决不是硬刚,而是降频加随机间隔:把固定sleep(0.5)改成sleep(random.uniform(0.5, 1.5)),再加一个失败计数,连续失败超过阈值就暂停几分钟。这是最朴素的礼貌采集策略,比任何花哨手段都稳。
6.4 可视化图表在别人电脑上打不开
现象:你生成的 HTML 图表,发给别人打开是空白。原因是 pyecharts 默认从 CDN 加载 JS 资源,对方没网或 CDN 被墙就白屏。解决办法是用--embed-js或把 JS 资源内联进 HTML,让文件自包含。这个坑在交付场景里特别致命,因为对方往往就是没网的环境。
6.5 依赖版本冲突导致启动即崩
现象:ImportError: cannot import name 'xxx'。原因是 pip 自动装了最新版,而代码是按旧版 API 写的。解决办法是严格按 requirements.txt 装,别用pip install -U手贱升级。如果已经升了,pip install 包名==版本号降回来,别试图改代码去适配新版,那是无底洞。
7. 进阶玩法:把分析系统从「能跑」推到「好用」
走到这里,系统已经能跑、能打包、能交付了。但如果你想让它在简历或实际工作里更有说服力,可以往三个方向推。第一个方向是加时间序列分析,把作品按上映时间排开,看票房和评分的趋势线,用 pandas 的rolling做移动平均,能平滑掉单部作品的波动,看出整体走势。第二个方向是加情感分析,把短评用 jieba 分词后做词频统计,或者接一个轻量情感模型,算出每部作品的「正面评价占比」,这个指标比单纯评分更能反映口碑结构。
第三个方向是做成可配置的分析流水线,把「采集-清洗-分析-出图」四步写成配置文件驱动的流程,用户改配置就能换分析维度,不用动代码。这一步做完,它就从「一个项目」变成了「一个工具」。
# 时间趋势的移动平均,窗口设 3 部作品 df["release"] = pd.to_datetime(df["release"]) df = df.sort_values("release") df["box_ma3"] = df["box"].rolling(window=3, min_periods=1).mean()window=3是因为开心麻花作品总量不大,窗口太大趋势线会太平,看不出变化。这个参数要根据你的数据量调,数据多就调大,数据少就调小。
最后说个我自己的习惯:每次改完分析逻辑,我都会拿一部已知结果的作品去验证,比如《夏洛特烦恼》票房和评分都高,如果我的散点图里它没落在右上角,那一定是代码或数据出了问题。这个「用已知答案反查流程」的习惯,帮我省下了无数次盲目调试的时间。希望帮到你。
本文还有配套的精品资源,点击获取