电影票房预测分析系统:Django+Echarts+机器学习完整实战
2026/9/10 8:48:08 网站建设 项目流程

每年到这个时间点,总能看到一批毕业生在论坛里蹲电影票房相关的毕设题目,一眼望过去全是“XX电影数据分析系统”“基于XX的票房预测”,但真正能落地跑通的寥寥无几。我自己去年帮带的一个学弟做的就是这个方向,题目就叫“电影市场预测分析系统”,技术栈是 Python + Django + Echarts,顺带加了一些简单的机器学习预测和可视化大屏展示。因为实际动手踩了不少坑,从数据采集清洗、数据库设计到图表渲染、模型预测调优,整个过程走下来有一套很顺的路径,今天就把这套完整方案整理出来,给正在做毕设或者想拿这个方向练手的同学一个可以直接参考的版本。

先说清楚这系统是干嘛的。它本质上是一个 Web 项目,后端用 Django 提供数据接口和页面渲染,前端用 Echarts 做票房数据的可视化展示,包括票房趋势、类型分布、地区占比、排行榜这些常规维度,同时接了一个简单的票房预测模块,基于历史数据做回归预测,用来估算一部电影上映后的票房区间。适合的人群很明确:计算机、大数据、人工智能方向的毕业生,尤其是想做数据分析 / 可视化 / 机器学习应用方向但又不想卷算法的同学,拿这个当作课设或者毕设的底子非常吃香。

1. 项目整体设计与技术路线

1.1 需求拆解:这个系统到底要做什么

毕设项目最怕的是需求写得不清楚,做到一半发现逻辑混乱。这个系统的核心需求其实可以拆成三块:数据管理、可视化分析、票房预测。

数据管理负责把电影信息、票房数据、上映时间、评分等抓下来存进数据库,提供增删改查的能力,这是 Django 最擅长的部分。可视化分析负责把数据库里的数据通过接口取出来,在前端用图表的形式呈现,Echarts 干的就是这件事。票房预测则需要基于历史数据训练一个简单的回归模型,让用户选择一部电影的属性,预测它上映后的票房量级。

从用户视角来看,系统大概有几个页面:数据总览大屏、票房趋势分析、电影类型分布、地区票房榜、电影热度分析、票房预测工具。每个页面对应一到两个核心图表,数据来源统一走后端接口,页面只负责渲染和交互。这个拆分的好处是前后端职责清晰,查 bug 的时候不会一头雾水。

1.2 技术选型:为什么是 Django + Echarts + 机器学习

很多初学的人一上来就往复杂了想,非要用 Spring Boot + Vue + Redis 才觉得有排面,结果自己根本不熟,最后连环境都配不明白。我建议稳住,用你自己最熟、社区资料最多的技术栈组合。

Django 的优势在于自带 ORM、Admin 后台、模板引擎和强大的生态。写一个博客型的数据展示网站,Django 不需要额外搭前端工程,一个 Template 模板就能渲染出完整页面,配上 DRF(Django Rest Framework)做接口,数据返回 JSON 给前端,非常顺手。Echarts 是百度开源的可视化库,图表类型丰富,文档齐全,配置项写起来也很直观,做毕设展示完全够了。

预测模块的话,不需要上深度学习那套东西,数据量不够,硬件也撑不住。用 Scikit-learn 的线性回归、随机森林或 XGBoost 做票房区间预测,既符合“人工智能”的题眼,又能实际跑出结果,答辩的时候也容易讲清楚算法原理。这套方案的综合成本低、周期短、效果上限高。

1.3 项目目录结构与模块划分

项目结构直接决定后续开发效率。我按 Django 的应用(App)来划模块,而不是把全部逻辑堆在默认的views.py里。大致结构是这样的:

movie_analysis/ ├── manage.py ├── requirements.txt ├── movie_analysis/ # 项目配置 │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── movies/ # 电影数据应用 │ ├── models.py │ ├── views.py │ ├── urls.py │ ├── admin.py │ ├── data_loader.py # 数据导入脚本 │ └── services/ │ └── predictor.py # 预测模型 ├── stats/ # 可视化分析应用 │ ├── views.py │ └── urls.py ├── templates/ │ ├── base.html │ ├── dashboard.html # 总览大屏 │ ├── trend.html ├── static/ │ ├── css/ │ ├── js/ │ └── echarts.min.js └── data/ ├── movies_clean.csv └── boxoffice.csv

这种结构的好处是:movies应用管电影和预测,stats应用管图表接口和页面路由,职责互不干扰。后面加功能不会越改越乱。

2. 数据层:票房数据从哪来、怎么洗

2.1 数据源选择与采集思路

做电影分析,首先得有数据。常见的公开数据源有 TMDB、豆瓣电影、猫眼电影、艺恩等。但毕设项目不建议直接写爬虫去抓实时数据,一方面反爬机制会让你头疼,另一方面数据不稳定,答辩现场万一挂了就尴尬。

我学弟当时用的是 GitHub 上一个公开的电影票房数据集,包含 2007 年到 2023 年的电影信息,字段有电影名称、上映日期、类型、制片国家、票房、评分、导演、主演等,接口是 CSV 和 SQLite 两种格式。下载后直接作为初始种子数据,后面再手工补充少量新片的数据。这个思路省时省力,又能保证数据质量。

如果一定要爬数据,不要头铁直接怼大站,找那些反爬弱的公开 API,比如 TMDB 的 API,注册个 key 就能拿数据。注意限速和请求头伪装,频控调低一点,能做到 2 秒一个请求最保险。

2.2 数据清洗与特征工程

数据拿到手以后千万不能直接灌数据库,要先洗一遍。常见的脏数据包括:缺失值、重复行、票房字段类型不一致、日期格式混乱、分类字段大小写不统一。

我用 Pandas 做了预处理,代码大致长这样:

import pandas as pd df = pd.read_csv('data/movies_raw.csv') # 去掉完全重复的行 df.drop_duplicates(inplace=True) # 票房统一为“万元”为单位的数值 df['box_office'] = df['box_office'].str.replace('亿', '').astype(float) * 10000 # 日期统一格式 df['release_date'] = pd.to_datetime(df['release_date'], errors='coerce') # 填充缺失评分,用该类型的均值 df['rating'] = df['rating'].fillna(df.groupby('genre')['rating'].transform('mean'))

特征工程上,我选了几个对票房影响明显的特征:电影类型、上映月份、档期(春节档 / 暑期档 / 国庆档 / 其他)、首日口碑评分、主演热度指数(针对顶级演员做映射权重)、导演历史票房均值。这些特征构造好以后存成新的表,后续预测模块直接调用,非常方便。

2.3 数据库模型设计

Django 的 ORM 模型是整个系统的地基,设计合理能省掉后面非常多的麻烦。我设计的核心模型有两个:电影基础信息表(Movie)和票房每日数据表(DailyBoxOffice)。

from django.db import models class Movie(models.Model): name = models.CharField(max_length=128, verbose_name='电影名') release_date = models.DateField(verbose_name='上映日期') genre = models.CharField(max_length=64, verbose_name='类型') country = models.CharField(max_length=32, verbose_name='制片国家') rating = models.FloatField(verbose_name='豆瓣评分') director = models.CharField(max_length=64, verbose_name='导演') box_office_total = models.FloatField(verbose_name='总票房(万元)') budget = models.FloatField(verbose_name='制作成本(万元)', default=0) actor_hot_index = models.FloatField(verbose_name='主演热度', default=0) class Meta: db_table = 'movie_info' ordering = ['-box_office_total'] class DailyBoxOffice(models.Model): movie = models.ForeignKey(Movie, on_delete=models.CASCADE) date = models.DateField(verbose_name='日期') daily_bo = models.FloatField(verbose_name='单日票房(万元)') screen_cnt = models.IntegerField(verbose_name='排片场次') class Meta: db_table = 'daily_box_office' unique_together = ('movie', 'date')

设计要点有两个。一是外键关系要明确,DailyBoxOffice 通过外键指向 Movie,这样查询一部电影的票房曲线只需要反向关联,不用额外建冗余字段。二是金额字段统一用 FloatField 存“万元”,不存成字符串,避免后面统计时反复转换。

3. 后端核心:Django 接口与业务逻辑

3.1 Django 项目初始化与应用拆分

环境这块其实是很劝退新手的点,先说下最稳的路径。用 virtualenv 创建独立的虚拟环境,然后安装 Django 4.2 LTS 版本、pandas、numpy、scikit-learn、djangorestframework、django-cors-headers、pymysql 这些依赖。requirements.txt 里写好版本号,换机器一键安装。

python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate pip install django==4.2 pandas numpy scikit-learn djangorestframework django-cors-headers pymysql

创建项目后,先配置 settings.py 的数据库、静态文件目录、模板目录和跨域设置。我是用 MySQL 还是 SQLite 呢?本地跑毕设强烈建议 SQLite,零配置,文件即数据库;如果服务器上部署且数据量大了再切 MySQL。Django 切换数据库非常方便,改一下 ENGINE 和 NAME 就行。

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } STATIC_URL = '/static/' STATICFILES_DIRS = [BASE_DIR / 'static']

3.2 数据查询接口实现

可视化页面需要的接口大致有这么几个:总票房统计概览、年度票房趋势、类型票房占比、地区票房 Top10、电影排行列表。这些接口用 DRF 写能省掉很多序列化的手写代码,但毕设如果不想引入太多框架,直接用 Django 的 JsonResponse 返回字典也是非常干净的。

我学弟这里用了 DRF 的 APIView,因为写起来更规范,答辩时也能提一句“基于 RESTful 风格的接口设计”。以票房趋势接口为例:

from rest_framework.views import APIView from rest_framework.response import Response from django.db.models import Sum from movies.models import DailyBoxOffice from datetime import datetime class BoxOfficeTrendView(APIView): def get(self, request): year = request.query_params.get('year', datetime.now().year) daily_data = ( DailyBoxOffice.objects .filter(date__year=year) .values('date') .annotate(total=Sum('daily_bo')) .order_by('date') ) dates = [d['date'].strftime('%m-%d') for d in daily_data] totals = [d['total'] for d in daily_data] return Response({'dates': dates, 'totals': totals})

这个接口输出的 JSON 结构是标准的{dates: [...], totals: [...]},前端 Echarts 拿到后直接把数组塞进 series,几乎没有二次处理成本。接口返回格式保持统一,是开发过程中很重要的一点。

3.3 票房预测模块的算法实现

预测模块是系统的亮点,也是“人工智能”体现得最明显的地方。我选了随机森林回归作为主力模型,因为它对特征数值范围不敏感,不容易过拟合,而且能输出特征重要性,答辩时可以解释哪些因素影响票房最大。

训练流程大致是这样:从数据库读电影数据,把类型、档期这类类别特征做 LabelEncoder 或 One-Hot,然后划分训练测试集,用 R2 和 MAE 评估效果。

import joblib import pandas as pd from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder def train_model(): df = pd.read_csv('data/model_features.csv') le = LabelEncoder() df['genre'] = le.fit_transform(df['genre']) df['slot'] = le.fit_transform(df['slot']) features = ['genre', 'slot', 'release_month', 'rating', 'actor_hot_index', 'director_avg_bo', 'budget'] X = df[features] y = df['box_office_total'] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) model = RandomForestRegressor( n_estimators=300, max_depth=12, min_samples_leaf=2, random_state=42 ) model.fit(X_train, y_train) print('R2:', model.score(X_test, y_test)) joblib.dump(model, 'movies/models/boxoffice_model.pkl') joblib.dump(le, 'movies/models/label_encoder.pkl') return model

预测的时候把用户输入的属性变量做同样的编码和拼接,调用model.predict()输出结果。为了让预测结果更容易理解,我把预测值转换成“预测票房区间”,比如 1.2 亿到 1.8 亿之间,而不是一个生硬的数字,用户一看就知道大致量级。这个技巧在答辩演示时效果很好。

4. 前端可视化:Echarts 图表实战

4.1 Echarts 引入方式与基本配置

Echarts 的引入方式有两种:一种是直接下载echarts.min.js放到 static 目录,另一种是 npm 引入。Django 项目用不着 npm,直接 script 标签引入本地文件是最简单可靠的。去官网下载构建版,注意选的是“完整版”而非“核心版”,否则地图组件可能缺失。

<script src="/static/js/echarts.min.js"></script>

引入后基本思路是:先给一个 div 容器设置宽高,然后echarts.init(dom)初始化实例,再调用setOption配置图表。setOption 是 Echarts 的灵魂,所有图表的样式、数据、交互都在这里配置。

4.2 票房趋势、类型分布、榜单三大图表

这三个图表是系统的门面,做出来以后页面效果立竿见影。

票房趋势图用折线图,用刚写的接口数据填进去。配置里要多用tooltiptrigger: 'axis',鼠标滑过能展示各个日期对应的票房,交互感非常好。平滑曲线改成smooth: true,观感高级很多。

类型分布图用饼图。注意饼图的 data 要传{name: '动作', value: 1200}这种格式,和折线图不一样。用radius: ['30%', '70%']配置一个环形饼图,比实心饼图好看,还能把中间区域空出来放总票房数字,这个设计很多大屏项目都在用。

排行榜用横向柱状图。横向柱状图的关键是把 xAxis 和 yAxis 的类型对调,数据量大的时候用yAxisinverse: true把最高票房的电影放在最上面。配置好 label 显示数值,效果非常直观。

const chart = echarts.init(document.getElementById('trendChart')); fetch('/api/boxoffice/trend/?year=2023') .then(res => res.json()) .then(data => { chart.setOption({ tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: data.dates }, yAxis: { type: 'value' }, series: [{ name: '总票房(万元)', type: 'line', smooth: true, areaStyle: {}, data: data.totals }] }); });

4.3 可视化大屏布局与交互

既然系统叫“可视化分析”,页面就不能做成传统的表格堆列表,至少有一个页面要像大屏的样子。做法是网格布局,把页面分成多个卡片区域,每个卡片放一个图表,背景用深色,标题区域加一排统计卡显示总票房、总电影数、评分均值等指标。

布局上我直接用 CSS Grid,四列两行,整体自适应。深色背景下 Echarts 的轴线颜色设置为浅色,文本颜色也是近白色。每个图表之间留出间距,视觉上不会挤在一起。

交互部分做了两个小功能。第一个是点击饼图的某一个扇区,下方会联动显示该类型下票房前十的电影列表;第二个是年份下拉框切换,折线图和排行榜一起刷新。这些交互用 Echarts 的chart.on('click', params)事件实现,拿到参数以后调用对应的接口更新其他图表。这些细节做出来,答辩时老师问“有没有交互功能”你就可以直接现场演示了。

5. 系统部署与运行全流程

5.1 本地运行三步走

跑起来是一个项目最基本的体面,也是很多同学卡住的地方。本地运行基本上三步:

第一步,把数据导入数据库:

python manage.py makemigrations python manage.py migrate python manage.py shell -c "from movies.data_loader import load_data; load_data()"

第二步,启动开发服务器:

python manage.py runserver 0.0.0.0:8000

第三步,浏览器访问 http://127.0.0.1:8000 ,先看首页大屏能不能正常出图,再看接口有没有报错。

这三步如果顺畅,说明环境、数据库、数据导入都没问题。如果哪一步卡住,先看命令行报错信息,99% 的情况是缺依赖或者数据库没迁移,按照报错找解决方案很快。

5.2 线上部署要点

毕设如果只要求演示,本地跑完全足够。但如果老师要求部署到服务器,或者你想放到线上展示给朋友看,那就得做部署。

Django 部署的方案通常是用 Nginx + Gunicorn + MySQL。核心流程是:项目代码传到服务器,创建虚拟环境装依赖,迁移数据库,然后用 Gunicorn 启动 Django,把它配到 Nginx 反向代理,静态文件由 Nginx 直接托管。

配置 Nginx 的时候最容易踩的坑是静态文件 404。解决方式是在 settings.py 里设置STATIC_ROOT,然后执行python manage.py collectstatic把所有静态文件集中到一个目录,再让 Nginx 指向这个目录。这一步一定要在部署前做,否则页面打开全是裸的。

需要留意的是 DEBUG 要改成 False,ALLOWED_HOSTS要加上你的服务器 IP 或域名。还有一些安全设置比如 SECRET_KEY 不要硬编码在源码里,可以用环境变量代替。安全设置不一定要很深,但至少不该让项目以明显不安全的配置直接裸奔。

6. 常见问题排查与避坑实录

6.1 Django 跨域与请求问题

写接口的时候最常见的报错是跨域。前端页面在本地开 8000 端口,请求接口如果走了另一个端口或者线上域名,浏览器就会拦。开发阶段直接装django-cors-headers然后CORS_ALLOW_ALL_ORIGINS = True,演示没压力。但如果生产环境也放开所有域名,其实是有安全风险的,所以线上建议配置白名单。

还有一个容易忽略的坑是模板引擎在渲染 JSON 时把括号转义成了 HTML 实体,导致前端拿到&quot;之类的字符。这个问题的根源是用了{{ data }}直接渲染,正确做法是把 JSON 传到json_script过滤器,或者用 fetch 拉接口,不要直接嵌入模板。

6.2 Echarts 图表不显示问题

Echarts 图表不显示,90% 的原因都是容器没有高度。div 如果只设置了宽度没设置高度,echarts.init能成功但图表区域为 0,看起来就是一片空白。初始化之前用 Chrome 开发者工具看一下容器的高度,如果是 0,就给 div 写死一个height: 400px,问题就解决了。

另一个高频问题是图表数据格式不对。后端返回的某个字段多了一层嵌套,或者日期格式是时间戳,导致 x 轴数据全是null。我排查的方法是打开浏览器的网络面板,看一眼 API 返回的 JSON 长什么样,再拿 DevTools 的 Console 打印一下setOption的数据,基本定位一遍就稳。

6.3 预测模型过拟合与数据偏差问题

模型预测这块常见的坑是数据量太少导致过拟合。电影样本只有两三百条时,随机森林在训练集上分数很高,测试集却拉胯。解决方式是加正则参数:min_samples_leaf调大、max_depth限制深度,还可以开max_features='sqrt'做随机特征选择。我用下来的经验是max_depth=10~12min_samples_leaf=2~4在小样本上比较稳。

还有一个更隐蔽的问题是训练集和预测时特征不一致。比如训练数据里有类型为“纪录片”,预测模块的用户输入没有这个选项,LabelEncoder 硬编码就会报错。我的做法是在保存模型的同时把编码器对象一起保存,而且所有类别以classes_为准,预测前统一用同一个编码器 transform,不在预测接口里重新 fit。

实际操作中还有一个我挺推荐的小优化:模型训练完以后把特征重要性输出到前端展示,告诉用户“评分和主演热度对票房影响最大”,这比单出个预测数字更有说服力,答辩时老师一听就知道你是真做了分析而不是死套模型。

整套系统做下来,前后大概花了两周时间,其中数据清洗和模型调优占了一大半,写代码本身并没有想象中那么慢。我现在回头看,这个项目最大的价值不是“演示效果多炫”,而是它把 Web 开发、数据库设计、数据分析和机器学习串在了一个完整流程里,让你把课堂上的碎片知识拼成了一整块。如果后续还想扩展,可以往推荐系统方向加一点用户观影偏好推荐,或者用爬虫定时更新每日票房数据,让系统真正“活”起来。做毕设最忌讳的是闭门造车,中间卡住的时候多去查别人的开源项目,看看别人怎么组织代码、怎么处理边界情况,很多问题都能迎刃而解。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询