Python电影票房预测系统实战:从爬虫到多项式拟合

发布时间:2026/9/24 12:19:47
Python电影票房预测系统实战:从爬虫到多项式拟合 简介一份基于Python的电影票房预测系统设计文档面向Python学习者、毕业设计者及影院信息化相关开发人员针对传统影院依赖人工经验排片、票房预估不准导致损失的问题提供完整的技术解决方案。文档以PDF形式提供共1个文件大小约1.17MB包含中英文摘要、关键词、目录及正文结构清晰便于按章节快速查阅。内容围绕系统设计与实现展开先通过BeautifulSoup、Scrapy等爬虫技术从中国电影网采集历史票房数据再利用Pandas进行缺失值与异常值处理、Numpy完成归一化标准化然后使用SciPy的curve_fit进行多项式曲线拟合建模通过调节多项式阶数适配不同票房变化趋势并涵盖Flask/Django界面展示、APScheduler定时更新等工程化要点给出了从数据获取到预测落地的完整链路。已有2583人学习下载适合希望快速理解票房预测系统设计思路、掌握爬虫与回归算法综合应用的读者。1. 票房预测不是玄学这套 Python 系统到底预测了什么“电影票房预测系统”这个词听起来很唬人但拆开看就是一条流水线用爬虫把历史票房抓下来存成表格再用多项式曲线拟合去描一条趋势线最后把某部电影已上映的票房数据代入推后面几天的走势。这套基于 Python 的实现真正的价值不在于“预测准不准”而在于它把影院排片从“靠个人经验赌”变成了“有数据曲线作参考”。适合三类人想拿 Python 做数据分析实战的初学者、需要给毕业设计或课程设计搭一个完整系统骨架的同学、以及真在影院或发行方做排片辅助的从业者。它不依赖 GPU不需要深度学习的黑匣子环境就是常规的 Python 3 Pandas NumPy SciPy Requests在 VS Code 里配好环境就能跑——这也是它最容易复现的原因。2. 三层架构拆解从爬虫到多项式拟合的完整链路2.1 系统的三个模块和各自边界论文里把整个系统划分为网络爬虫子模块、数据存储子模块和票房预测子模块这个划分在工程上也是合理的。爬虫模块负责从中国票房网58921.com按时间戳分页抓取电影基本信息和每日票房存储模块把清洗后的数据整理成 CSV 文件作为预测模型的输入预测模块用多项式曲线拟合算法训练“票房走势模型”然后把待预测电影的前期票房套进模型计算。三个模块之间是严格单向依赖的爬虫不关心预测逻辑预测模块不关心数据怎么来的。这种松耦合设计对学习来说非常友好你可以单独替换掉任何一个模块——比如把爬虫部分换成读本地 Excel或者把多项式拟合换成线性回归都不会影响其他部分。实际做项目时我一般建议先跑通这条单向链路再回头优化细节。2.2 数据来源和特征字段的实际情况系统的数据来源是中国票房网的“网票票房”页面按页分页展示历史数据。初始数据集的规模不大电影基本信息 10 个字段发行时间覆盖 2015 年到 2018 年历史票房数据 1587 条。字段主要包括电影名称、发行时间、每日票房数值这些就是预测模型的全部输入。表初始数据字段说明数据类别数量/范围说明基本信息10 个字段片名、发行时间、类型等发行时间2015 年 - 2018 年训练数据的时间跨度历史票房数据1587 条每日票房记录按天粒度这个数据量级意味着多项式拟合的阶数不能太高后面会专门讲因为 1587 条数据看着不少但按电影拆分后单部电影的有效数据点可能只有几十个高阶多项式在这种小样本上极易过拟合。论文里的代码示例用了 10 阶多项式这在 demo 里可以真要上线预测需要谨慎评估。2.3 为什么选多项式曲线拟合而不是别的算法论文对比了两种经典方法基于人口统计学的研究方法和基于 MAAP 评级的回归模型。前者靠调查问卷分析观众偏好无法量化成连续的时间序列预测后者引入了奥斯卡奖项、MPAA 评级、市场集中度等因子做线性回归理论上更“正规”但这些因子数据很难实时获取——奥斯卡一年一次MPAA 评级也不是每部电影都有公开数据。多项式曲线拟合的优势在于只需要票房本身的历史数据不需要额外的因子数据来源单一且爬取稳定。拟合的本质是最小二乘法——最小化误差平方和找到一条多项式曲线使已知票房数据点到曲线的垂直距离平方和最小。Python 里 NumPy 的np.polyfit和 SciPy 的curve_fit都能做这件事前者更直接输入 x 和 y 数组、指定阶数返回多项式系数。3. 把中国票房网的数据搬到本地Requests 爬虫与解析落地3.1 模拟浏览器请求头的基本姿势论文里的网络爬虫子模块核心逻辑是初始化请求头伪装成浏览器访问目标网址拿到 HTML解析出电影 URL 列表再逐个进入电影详情页抓取片名和票房。这里最关键的细节是请求头伪装网站对纯程序请求很敏感不带User-Agent的请求大概率直接返回 403。以下是论文给出的核心爬取函数我保留了原始结构并补了注释import requests def getHTMLText(url): # 伪装成 Chrome 浏览器的请求头 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36 } try: r requests.get(url, headersheaders) r.raise_for_status() # 响应状态码不是 200 时抛出异常 r.encoding r.apparent_encoding # 自动识别网页编码避免中文乱码 return r.text except requests.RequestException as e: print(抓取出错:, e) return 这段代码有两个容易被忽略的点。第一r.encoding r.apparent_encoding是处理中文乱码的关键很多新手直接用r.text遇到 GBK 编码的页面就会得到一堆乱码票房数字和电影名全变成符号。第二raise_for_status()会在状态码不是 200 时直接抛异常这样你不需要手动写 if 判断异常会被统一捕获并打印。3.2 分页遍历和电影 URL 收集主流程是按页遍历“网票票房”列表页收集所有电影详情页的 URL再逐页解析。论文里的代码是这么写的def main(): uinfo [] # 爬取前 11 页的列表页 for i in range(11): url fhttp://58921.com/daily/wangpiao?page{str(i)} html getHTMLText(url) fillUnivList(uinfo, html) # 从 HTML 中解析出电影 URL 存入列表 printHtml_csv(uinfo) # 将结果写入 CSV 文件for i in range(11)表示只爬前 11 页这里要说明的是列表页是分页展示的每页有一定数量的电影条目实际爬取时你需要根据目标时间范围调整页码上限。fillUnivList函数内部通常用正则表达式或 BeautifulSoup 提取a标签的href属性拼接成完整的电影详情页 URL。我自己的习惯是用 BeautifulSoup 的select(a[href])做选择器比正则更抗页面结构变化。3.3 数据解析正则、BS4、lxml 怎么选论文明确提到可以“自主选择 re、bs4、lxml 中的一种解析方法”。我在实际复现时的经验是页面结构稳定、目标字段简单比如只要电影名和票房数字——用正则最直接性能也最好页面结构复杂、需要按层级定位——用 BeautifulSoup 的find_all或select更直观追求解析速度、处理大量页面——用 lxml 的xpath比 BS4 快好几倍。票价页面里票房数字通常在某个td或span标签内解析时要先确认目标标签的唯一性。如果发现同一个 class 名被多处使用就需要加上父级节点限制或者直接改用 xpath 的contains(text(), 票房)这种条件定位。这一步没有统一标准本质上是跟目标网站的 HTML 结构博弈页面改版了解析代码就要跟着改。4. 数据清洗与 CSV 入库Pandas 处理的取舍和模型输入规范4.1 为什么不能把爬到的数据直接喂给模型爬虫拿到的原始数据至少有三个问题重复数据同一部电影可能被多个列表页收录、特殊符号表情、外部链接、HTML 标签残留、无效字段缺失片名或票房为空的记录。论文里的数据预处理逻辑是先去重保证影片信息单一性再清洗特殊符号最后做有效性校验——有效的存入字典输出给下一模块无效的直接丢弃。这一步用 Pandas 做最方便。import pandas as pd def clean_data(df): # 1. 去掉重复数据以电影名称为去重键 df df.drop_duplicates(subset[movie_name], keepfirst) # 2. 去除票房列中的特殊符号和非数字字符 df[box_office] df[box_office].str.replace(r[^\d.], , regexTrue) # 3. 清洗电影名中的空白符和异常字符 df[movie_name] df[movie_name].str.strip() # 4. 删除票房为空或无法转换为数值的记录 df df[pd.to_numeric(df[box_office], errorscoerce).notna()] return df这段代码里drop_duplicates(subset[movie_name])是按电影名去重keepfirst表示保留第一次出现的记录。str.replace(r[^\d.], , regexTrue)用的是正则表达式把票房字段里所有非数字和小数点的字符全部删掉——这一步很实用因为网页里的票房数字经常带着“万”“亿”或者千分位逗号。最后一行把无法转换为数值的票房记录过滤掉保证进入模型的都是干净数字。4.2 用 DataFrame 转置构造“电影名 × 上映天数”矩阵论文的数据存储子模块有一个关键操作用 pandas 的DataFrame设置列名columns和行名index分别存储电影名与历年票房然后转置存储为 CSV。翻译成人话最终 CSV 的结构是“行 电影名列 上映天数第 1 天票房、第 2 天票房……”这样每一行就是一条完整的票房走势序列可以直接拿来拟合。def save_as_csv(data_list, output_path): # data_list: [{movie_name: 流浪地球, day1: 100, day2: 150}, ...] df pd.DataFrame(data_list) df df.set_index(movie_name) # 电影名作为行索引 df df.T # 转置后行为电影名列为上映天数 df.to_csv(output_path, encodingutf-8-sig) print(f数据已保存到 {output_path})这里有两个细节值得注意。第一encodingutf-8-sig而不是utf-8——加了-sig之后 Excel 打开 CSV 不会出现中文乱码这个坑我踩过一次当时导出的 CSV 用 Excel 打开全是乱码用记事本打开却是正常的就是编码签名的问题。第二转置操作df.T会让数据类型从 numeric 变成 object如果后续要计算建议转置后用df.apply(pd.to_numeric)把所有列转回数值类型。CSV 是这个小项目最合适的存储介质数据量只有一千多条SQLite 属于杀鸡用牛刀但如果后续要支撑 Web 端查询建议早点迁到 SQLiteCSV 在并发读写时容易损坏。4.3 训练集和预测集的切分逻辑系统做预测时是把“某一对比电影的历史票房”和“待预测电影的前期票房”作为输入套进拟合出的模型里计算。这里隐含了一个切分逻辑对比电影的数据全部作为训练集待预测电影已上映的票房数据作为已知条件未上映的部分是预测目标。实际实现时我习惯把待预测电影的数据按时间切一刀上映前 N 天的数据作为模型输入第 N1 天之后作为验证集用来评估预测误差。这个 N 一般取 5 到 10因为上映首周的票房走势基本决定了后续走势这也是影院排片最需要参考的时间窗口。5. 多项式曲线拟合的实战np.polyfit、可视化与常见排查5.1 核心拟合代码的逐行拆解论文里的预测核心代码是用np.polyfit做多项式拟合np.poly1d构造多项式函数再用 Matplotlib 画拟合曲线。我把它整理成可直接运行的形式import numpy as np import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties font FontProperties(fnameC:\Windows\Fonts\simhei.ttf) # 中文字体 def draw_fit(data, k10): 对票房序列做 k 阶多项式拟合并绘制拟合曲线 data: 某部电影的每日票房列表例如 [100, 150, 120, 200, ...] x np.array(range(len(data))) # 上映天数0, 1, 2, ... y np.array(data, dtypefloat) # 每日票房 z np.poly1d(np.polyfit(x, y, k)) # k 阶多项式拟合 return z # 示例拟合《流浪地球》的票房走势 movie_data data[流浪地球].dropna().tolist() z draw_fit(movie_data, k10) x_fit np.linspace(0, len(movie_data), 100) plt.plot(range(len(movie_data)), movie_data, b-, label实际票房) plt.plot(x_fit, z(x_fit), r-, label拟合曲线) plt.xlabel(上映天数) plt.ylabel(票房万元) plt.legend() plt.show()np.polyfit(x, y, k)的返回值是多项式系数数组从高次项到常数项排列np.poly1d把这组系数包装成可直接调用的多项式函数对象z(x_fit)就是计算多项式在给定 x 坐标处的值。np.linspace(0, len(movie_data), 100)生成 100 个均匀分布的 x 坐标让拟合曲线更平滑。这里的k10就是多项式阶数后面会重点说这个参数。5.2 三个常见的坑从拟合失效到负数票房避坑部分直接上结论下面三条是我在复现论文时实际踩过的都是“现象 → 原因 → 解决”的结构第一条坑拟合曲线严重偏离真实数据甚至出现大幅震荡。原因是多项式阶数 k 太大比如论文代码里的 10 阶当单部电影的有效数据点只有 20 到 30 个时10 阶多项式会强行穿过每一个点导致曲线在端点处剧烈抖动——这个现象在数学上叫龙格现象。解决办法是把 k 降到 3 到 5 之间或者用分段拟合。我现在做这类预测默认先从 k3 开始试看拟合优度 R² 和残差图再决定是否升阶。R² 是论文里提过的拟合优度概念大于 0.8 算拟合较好但小样本下 R² 高也可能是过拟合两者要结合看。第二条坑预测出的票房出现负数。原因是多项式拟合的本质是数学函数外推不受“票房必须大于等于零”的业务约束当阶数高或数据波动大时拟合曲线在尾部下探就能算出负值。我踩过一次某部电影上映 15 天后的预测票房是 -230 万当时还以为是代码算错了最后查数据发现是模型外推区间太大。解决思路有两个——要么把预测区间限制在已有数据的 1.5 倍范围内不要外推太远要么在代码里对预测结果做截断处理np.clip(prediction, 0, None)把负值直接归零。这不是最优雅的做法但至少不会输出反常识的结果。第三条坑dropna()之后报“array length mismatch”错误。原因是不同电影的票房序列长度不一样有的电影上映 20 天有的上映 100 天直接放进同一个数组里做多项式拟合维度对不上。解决方法是拟合前统一取前 N 天的数据N 取所有目标电影的最小上映天数或者对票房序列做长度归一化。我一般取前 30 天——绝大多数电影的票房峰值都出现在这个窗口内超过 30 天之后每日票房已经低到对排片没有参考意义。5.3 环境配置的注意点论文的测试环境就是标准的 Python 数据科学栈重点是版本匹配。用pip install pandas numpy scipy requests matplotlib一条命令装齐如果是 Python 3.10 以上版本注意 NumPy 不要装太旧的版本否则np.polyfit的默认精度参数会报警告。另外matplotlib画图时中文标题会显示成方框需要像 5.1 代码里那样显式指定中文字体路径或者用plt.rcParams[font.sans-serif] [SimHei]全局设置。Windows 下中文字体路径是C:\Windows\Fonts\simhei.ttfMac 上是/System/Library/Fonts/PingFang.ttc。6. 拟合阶数 k 的边界判断怎么算“够用”而不是“过拟合”多项式拟合里最让人纠结的就是阶数 k选小了曲线太直完全跟不上票房先升后降的趋势选大了曲线精确穿过每个数据点但预测下一周直接崩掉。我现在的判断习惯是三步走。第一步画残差图。把拟合值减去实际值如果残差呈现明显的“波浪形”或“喇叭形”说明曲线形状不对——前者表示阶数不够后者表示数据波动太大需要先平滑。这一步不用写复杂代码plt.scatter(x, y - z(x))一行就够重点是看分布形状而不是具体数值。第二步交叉验证。把某部电影前 70% 的票房数据拿来拟合后 30% 当验证集计算平均绝对百分比误差MAPE。k 从 2 逐次加到 8每次记录 MAPE选误差最小的那个 k 值。这个办法比看 R² 可靠得多因为 R² 只说明拟合曲线对已知数据的解释程度不能说明外推能力。第三步业务约束。无论 k 是多少预测结果必须满足两个常识票房不可能为负票房曲线整体趋势是衰减的除非有口碑逆袭或档期刺激但那种情况任何算法都预测不了。如果模型输出违反这两个约束不用调参直接改代码做边界修正。另外如果你想把系统做得更完整可以加两个低成本功能。一个是定时更新——用APScheduler每隔几小时重新爬一次当天票房并更新 CSV这样预测数据就不会滞后。另一个是简单的 Web 展示——用 Flask 包一层接口把最强电影票房走势图渲染成页面这正是热搜里“python 数据分析与可视化”的常见用法不需要动态更新静态刷新就够用代码量能控制在一百行以内。说回踩坑这事我现在每次做完一个预测项目都会强制走一遍“残差图 交叉验证 边界检查”这三板斧哪怕数据再简单也不过这关不交活。当年用 10 阶多项式算出一堆负数票房的教训太深刻了从那以后我对模型的敬畏心明显多了。希望这套方法能帮你在复现论文时少走点弯路。本文还有配套的精品资源点击获取