ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

电影市场分析系统毕设全解析:爬虫、数据库与可视化实战

电影市场分析系统毕设全解析:爬虫、数据库与可视化实战 答辩结束一周终于有时间把这个电影市场分析系统——我的毕设项目整理成文。当初选这个题目其实没有太多纠结我本身就喜欢看电影毕业设计又想要一个数据能公开获取、可视化效果直观、讲起来有内容的题目逛了一圈下来发现电影市场分析几乎是完美答案。数据源到处都是技术栈可深可浅既能展示爬虫能力又能体现数据库设计和数据可视化水平最关键的是——答辩的时候评委老师大概率也看过这些电影聊起来不费劲。这篇文章不是那种纯配置说明文档我打算从选题动机、系统设计、爬虫实现、数据库建模、后端接口、前端可视化到论文配合把整个项目的来龙去脉都摊开讲。所有代码和配置我都整理进了毕设源码包里文章里我也会把核心代码片段贴出来方便你直接在本地跑起来看效果。如果你正在纠结毕业设计做什么或者想做数据分析类项目但不知道怎么下手这篇内容应该能帮你少走不少弯路。1. 毕设选题的来龙去脉从看电影到分析市场1.1 为什么电影市场方向值得做很多同学选毕设题目要么跟导师要一个看起来很高深的课题要么随便从往届项目里找一个抄。我的建议是尽量选数据源容易获取、业务逻辑你自己能讲清楚、可视化效果一眼能看出工作量的题目。电影市场恰好满足这三点。先说数据源。电影相关的公开数据非常丰富上映日期、票房、评分、类型、导演、演员、制片地区这些字段结构化程度高爬下来基本是现成的表格数据不需要做太多非结构化处理。相比爬电商评论、爬微博热搜电影数据的反爬压力小很多网站对单人低频爬虫的容忍度高得多非常适合毕设阶段的爬虫练手。再说业务逻辑。电影市场的分析维度天然就多可以做票房时序趋势、电影类型占比、评分分布、档期效应分析、口碑与票房相关性甚至可以做导演、演员的票房号召力排名。分析维度的数量直接决定了系统功能模块的饱满度而功能模块饱满度又是答辩评分的重要参考。最后是可视化。ECharts对时序折线图、饼图、柱状图、雷达图的支持非常成熟电影的票房曲线做出来本身就很漂亮而且逆跌档期效应这些都是电影行业里很真实的概念解释起来既有专业感又能让评委老师听得懂。1.2 需求拆解一个合格的毕设系统该有哪些功能选题定完之后我第一件事不是写代码而是把需求拆成模块。毕设最忌讳的就是想到哪写到哪最后代码乱成一锅粥论文也不知道从哪一章开始写。我当时按数据从哪里来、数据存在哪里、数据拿来干什么、结果怎么展示这条线把系统拆成了五个模块数据采集模块爬取电影的基础信息、票房信息和评分信息支持手动触发和定时增量采集。数据存储模块设计MySQL数据库建立电影主表、票房记录表、评分明细表等核心表结构。数据清洗模块处理缺失值、重复记录、格式不统一等问题保证分析结果可信。数据分析模块按档期、类型、地区、口碑等维度完成统计聚合与相关性计算。可视化展示模块用前端图表展示各维度的分析结果并提供简单的筛选交互。模块对应技术点答辩可提问点数据采集requests JSON接口解析反爬策略、增量采集方案数据存储MySQL 8.0 三范式表结构设计理由、索引优化数据清洗pandas空值处理策略、去重逻辑数据分析SQL聚合 Python统计分析方法为什么选这个维度可视化Vue ECharts图表的数据流如何打通这个功能矩阵我建议你在做之前也列一张后面写论文的时候直接对应系统功能需求分析那一章一个字都不用改。2. 技术栈选型和系统架构为什么我坚持用这套组合2.1 技术选型对比Python和Java到底选哪个说实话毕设系统用Java Spring Boot也能做我们班很多同学就是这么交的。但我在选型的时候反复比较过两条路线最后还是选了Python的Flask。原因有三条供你参考。第一爬虫生态。电影数据采集必然要用爬虫Python的requests、BeautifulSoup、Scrapy这套组合太成熟了写起来几十行就能搞定一个爬虫脚本。Java当然也能写爬虫但HttpClient加Jsoup写起来模板代码多断点续爬、日志记录这些都得自己搭。毕设时间有限把精力省给数据分析模块更划算。第二数据处理能力。爬下来的数据必须清洗pandas处理DataFrame是Python的看家本领。空值填充、重复值删除、字段类型转换一条链式调用就完成了。用Java做同样的事情哪怕用Stream API代码量也是Python的几倍。第三答辩叙事的连贯性。用Python生态我可以在论文里把爬虫-清洗-分析-可视化串成一个完整的Python数据科学生命周期用Java的话爬虫和分析是两套技术体系论文写起来容易松散。当然选择Flask而不是Django也有考量。Flask足够轻量适合像电影市场分析系统这样以接口返回JSON为主的项目Django自带Admin后台和ORM但这些重量级功能我们用不太上反而增加理解成本。我的项目用Flask Flask-CORS PyMySQL整个后端可以压缩到几个清晰的文件里。2.2 系统整体分层设计整个系统是典型的前后端分离架构。前端是一个Vue3 Vite ECharts的单页应用通过axios请求后端接口获取JSON数据后端是Flask应用按蓝图Blueprint拆分成采集、分析、查询三个模块统一返回JSON格式的响应数据库是MySQL核心存三张业务表加若干维表。数据流向是爬虫脚本从目标站点采集原始数据经清洗后写入MySQL后端查询接口从MySQL聚合数据并以JSON返回前端将JSON渲染成图表。这个分层的好处是每一层都能独立测试。我在答辩的时候专门讲了一点爬虫和数据清洗可以脱离Web系统单独运行数据分析模块也可以直接用脚本验证结果Web展示层只是把分析结果暴露出来的一个窗口。评审老师很认这种高内聚低耦合的设计因为我确实把模块间的依赖控制到了最低。2.3 为什么不完全照搬现成的开源项目GitHub上有大量电影数据分析的开源项目光电影数据分析系统关键词就能搜出一堆。但我的建议是参考开源的思路和代码风格但最终交付的源码必须是你一行行能讲清楚的。原因很实际答辩现场演示的时候老师随手点开你源码里一个函数问这段是干嘛的如果是从别处抄来的逻辑讲解会非常勉强。反而是自己写的代码哪怕丑一点但因为踩过坑每一行都能说出个所以然。我的做法是参考了几个开源项目的表结构和ECharts配置但数据清洗逻辑、爬虫调度、分析维度的业务规则全部自己设计最后源码里每一段逻辑都经得起追问。3. 数据采集层爬虫设计与反爬避坑实录3.1 数据源怎么定要抓哪些字段选数据源是爬虫的第一步也是最需要谨慎的一步。我之前想过直接爬猫眼的热门榜单但发现列表页只有部分字段详情页的信息又分散后来放弃了通用榜单改从公开的票房数据接口和电影详情接口分别获取数据。最终确定的字段分三组电影基础信息包括电影名称、上映日期、导演、主演、类型、制片地区、时长票房信息包括单日票房、累计票房、上映天数、排片场次、场均人次口碑信息包括评分、评分人数、想看人数。这些字段加起来大概是16个左右覆盖了后续分析模块所需的大多数维度。在定字段的时候有个技巧先想清楚你后面分析要用什么字段再去爬什么数据不要盲目追求字段数量。我这个项目的分析维度一开始就定好了所以爬虫字段表是直接从分析需求反推出来的。比如我要做口碑-票房相关性分析就必须同时爬到每个电影的评分字段和累计票房字段我要做档期效应分析就必须抓到精确到日期的上映时间这样才能划分春节档、暑期档、国庆档。3.2 爬虫框架选择requests BeautifulSoup还是直接解析JSON很多教材教爬虫都是BeautifulSoup解析HTML但真实项目中绝大多数现代网站的前端数据都是通过Ajax接口加载的直接解析JSON往往比解析HTML更高效、更稳定。我一开始也是老老实实解析HTML后来发现页面结构一小改解析逻辑就全崩接口反而变化不大。所以我的最终方案是先用requests模拟浏览器请求拿到JSON结构再用pandas直接读取JSON里的列表字段。HTML解析只保留了一个兜底逻辑用来处理接口偶尔拿不到数据的情况。import requests import pandas as pd def fetch_daily_box_office(date_str): url https://example-api.com/boxoffice/daily params {date: date_str} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0, Referer: https://example.com/, } resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() # 假设返回结构中 data.list 是票房记录列表 return pd.DataFrame(data[data][list])这个函数是整套爬虫的地基。我在实际写的时候把headers单独提出来配置因为不同接口需要的请求头不完全一样集中管理方便后续维护。3.3 动态加载参数找到稳定字段是关键电影详情页的字段有些是分页接口按需加载的比如累计票房的实时变化。爬虫代码本身不难难在要把接口的返回结构完全摸清楚。我在抓数据的时候发现不同接口的返回字段命名非常不统一有的叫movieName有的叫movie_name有的接口里日期是时间戳有的接口又是2024-05-01格式的字符串。这个过程很琐碎但必须记录下来后续清洗环节全靠这份字段映射关系表。我的建议是在爬虫正式跑全量数据之前先用脚本把一份样本数据落到本地手工检查一遍字段类型和取值范围。这一步能省下后面数据清洗的大量返工时间。我第一次没有做这个检查结果入库之后发现日期字段混了两种格式导致SQL排序全是乱的只能清库重爬。3.4 频率控制和异常处理爬虫要在能跑和不封之间找平衡爬虫的访问频率是新手最容易忽略的问题。爬太快容易被封IP爬太慢数据采集耗时太长。我的做法是设置一个可配置的请求间隔默认是1.5秒到3秒之间的随机值模拟真实用户的操作节奏同时加上重试机制和请求日志。import logging import random import time logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[logging.FileHandler(crawler.log), logging.StreamHandler()], ) def safe_request(url, params, headers, retries3): for attempt in range(retries): try: time.sleep(random.uniform(1.5, 3.0)) resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() return resp.json() except requests.exceptions.RequestException as e: logging.warning(f请求失败第{attempt 1}次重试: {e}) time.sleep(5) logging.error(f请求最终失败: {url}) return None这个safe_request函数是我整个爬虫模块里复用最多的工具。它的核心价值不只是重试而是把请求失败这件事变得可见——日志文件里记录了每一次失败和重试的时间、URL和异常信息后期排查的时候才能知道是哪一类请求出了问题。3.5 断点续爬让失败不变成重来爬电影数据不是一次跑完就结束的因为电影市场是动态更新的今天有新片上映明天的票房数据就不一样。所以我设计了两个采集模式全量采集和增量采集。全量采集用于首次获取历史数据增量采集用于每天定时补充前一天的票房和新上映电影。增量采集的关键是断点续爬——把已经处理过的页码和日期记在数据库里下次运行自动跳过不会重复请求。我用一张crawl_log表来记录每次采集的状态字段包括采集类型、开始时间、结束时间、抓取数量、状态、备注。这个表的业务价值很大论文里系统可靠性设计那一节我直接引用了这张表的设计和日志截图说明系统具备可追溯、可恢复的能力比空谈可靠性强太多。4. 数据库与数据清洗从脏数据到分析宽表4.1 核心表结构设计数据落库是承前启后的环节表结构设计得好不好直接决定后面SQL写起来痛不痛苦。我的数据库有三张核心表movie,box_office_record,rating_record另外加一张crawl_log用于记录采集状态。-- 电影主表 CREATE TABLE movie ( movie_id INT PRIMARY KEY AUTO_INCREMENT, movie_name VARCHAR(128) NOT NULL, release_date DATE, director VARCHAR(128), actors VARCHAR(255), genres VARCHAR(128), region VARCHAR(64), duration INT, movie_type VARCHAR(32), -- 例如: 剧情 / 动作 / 动画 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_movie_name (movie_name, release_date) ); -- 票房记录表 CREATE TABLE box_office_record ( record_id INT PRIMARY KEY AUTO_INCREMENT, movie_id INT NOT NULL, stat_date DATE NOT NULL, daily_bo DECIMAL(12, 2), -- 当日票房万元 cumulative_bo DECIMAL(12, 2), -- 累计票房万元 show_count INT, -- 排片场次 avg_viewers DECIMAL(10, 2), -- 场均人次 FOREIGN KEY (movie_id) REFERENCES movie(movie_id), UNIQUE KEY uk_movie_date (movie_id, stat_date) );movie_id和stat_date的组合唯一键非常关键它保证了同一个电影同一天不会插入两条票房记录。后面做增量更新的时候直接INSERT ... ON DUPLICATE KEY UPDATE就能完成去重和更新非常方便。4.2 数据清洗pandas的几行代码解决大问题数据入库之前我习惯在Python里先用pandas清洗一遍。清洗的流程固定四步去空、去重、改正类型、统一格式。def clean_movie_data(df): # 1. 删除电影名称为空的行 df df.dropna(subset[movie_name]) # 2. 按电影名和上映日期去重 df df.drop_duplicates(subset[movie_name, release_date]) # 3. 票房统一转成万元评分统一转成 float df[total_box_office] pd.to_numeric(df[total_box_office], errorscoerce) / 10000 df[rating] pd.to_numeric(df[rating], errorscoerce) # 4. 日期列统一成日期格式 df[release_date] pd.to_datetime(df[release_date], errorscoerce) return df这里有两个细节值得说。第一errorscoerce是pandas处理脏数据的救命参数遇到无法转换的值会变成NaN而不是报错配合dropna就能把异常行过滤掉。第二票房的单位转换一定要在入库前完成否则后面所有分析SQL都得除以10000或乘以10000非常容易出错。4.3 为什么建议单独建一张分析宽表细心的同学可能会问反正数据都在三张表里为什么还要单独建宽表我的理由是分析模块的SQL写起来会简洁很多。如果每次分析都要JOIN三张表SQL不仅长而且容易出聚合错误单独建一张analysis_wide_table把常用字段全部冗余进去分析时只需SELECT ... FROM analysis_wide_table WHERE ...即可。宽表不是建来好看的它能体现你对数据分析场景的理解。我当时把以下字段放进了宽表电影名称、上映日期、档期标签春节档/暑期档/国庆档/普通档、类型、地区、评分、评分人数、累计票房、首日票房、首周票房、上映天数。这些字段基本覆盖了后面所有分析模块的需求而且字段含义单一明确连我自己看SQL的时候都不用回想JOIN逻辑。5. 分析模块与后端接口让数据自己说话5.1 分析维度设计先用Excel验证再写代码系统的核心价值在分析而不是在展示。我在写后端接口之前先用Excel手工验证了一遍各个分析维度的结果发现有些维度看起来合理但实际做出来没什么意义。比如导演票房号召力分析因为样本里头部导演重复出现的次数太少做出来就是几个极端值没有统计规律于是果断砍掉替换成地区电影市场份额和类型平均票房对比这种结果更稳定的维度。最终保留的分析维度有六个年度票房趋势分析按年度汇总总票房展示电影市场的整体增长波动。档期票房对比按春节、暑期、国庆等档期分组比较不同档期的票房贡献。类型市场份额统计各类型的电影数量和票房总和观察观众偏好。评分分布分析按评分区间分组分析市场口碑分层情况。口碑与票房相关性计算评分与累计票房之间的相关系数验证口碑是否真正驱动票房。头部效应分析排名前10%的电影占据了多少票房份额分析市场集中程度。这些维度写进论文里非常能打因为每个维度都有一个明确的业务问题在背后支撑不是单纯地把图表堆在页面上。5.2 后端接口设计FastAPI之外的选择我后端用的是Flask并且按蓝图方式组织代码。虽然标题中提到了这些热搜词源码等等这里说明一下我们项目最终的交付物就是一套完整源码所以后端接口的代码风格尽量简洁易懂毕竟是自己要答辩讲清楚的代码。接口设计遵循一个小原则一个接口只回答一个分析问题。不要图省事把多个分析维度的结果塞进同一个接口里。我总共设计了8个接口分成两类基础数据查询接口和分析图表接口。接口路径方法功能描述返回格式/api/movie/listGET分页查询电影基础信息{ list, total }/api/movie/detail?idGET查询单部电影详情{ movie }/api/analysis/yearlyGET年度票房趋势数据{ categories, series }/api/analysis/boxofficeGET票房主力类型与档期对比{ data }/api/analysis/ratingGET评分分布和维度指标{ data }/api/analysis/correlationGET口碑-票房相关性数据{ data }核心分析接口的返回结构我全部用统一的{ code: 200, message: success, data: ... }包裹前端解析时逻辑非常统一。5.3 SQL聚合和相关性计算分析接口的核心是SQL和少量Python统计。以口碑-票房相关性分析为例这个接口的实现方案是先从宽表查出每个电影的评分和累计票房然后在Python中用pearson相关系数计算两者相关性。from scipy.stats import pearsonr def calc_rating_boxoffice_corr(): query SELECT rating, cumulative_bo FROM analysis_wide_table WHERE rating IS NOT NULL AND cumulative_bo IS NOT NULL df pd.read_sql(query, engine) if len(df) 30: return {correlation: None, sample_count: 0} corr, p_value pearsonr(df[rating], df[cumulative_bo]) return {correlation: round(corr, 4), p_value: round(p_value, 4), sample_count: len(df)}这里有个边界条件必须处理样本量太少的时候相关系数没有任何统计意义。所以我加入了len(df) 30的判断小于30直接返回空值。这种细节在答辩时是加分项说明你考虑过结果的可靠性而不是只会调库。6. 前端可视化图表不是堆上去就完事6.1 技术选型Vue3 Vite ECharts前端部分选了Vue3配合Vite。这个组合的优势是启动快、组件化清晰开发体验很好。图表库毫无疑问是ECharts它对异步数据的支持非常完善配置项灵活而且社区资源多遇到不会画的图基本一搜就有答案。选择Vue3而不是Vue2还有一个务实考虑很多组里同学都在简历里写了Vue3说明大家已经默认Vue3是主流了。我的毕设源码里也包含了前端完整项目答辩时现场npm run dev就能跑起来演示完全不需要额外配置。6.2 页面结构一张大屏展示所有分析结果我的前端是一个典型的数据大屏风格页面单页布局顶部是标题栏下面分四块区域展示不同的图表年度票房趋势图折线图横轴是年份纵轴是总票房。档期票房对比图柱状图按档期分组比较票房。类型分布图饼图展示不同类型电影的数量和票房占比。口碑-票房散点图散点图横轴是评分纵轴是累计票房每个点代表一部电影。除了图表还加了一个电影数据表格支持按电影名称搜索、按类型筛选方便评委老师在演示时随机查看某部电影的数据。表格和图表共用同一套接口切换筛选条件时所有图表联动刷新。这个交互设计不复杂但在演示时效果很好比一张静态大屏生动得多。6.3 ECharts使用中的三个坑图表组件跑通很轻松但那只是能显示要做到讲得清楚演示不翻车还得注意几个隐藏问题。第一个坑是异步数据更新时的图表动画闪烁。每次接口返回新数据ECharts默认会有入场动画但频繁刷新时动画会造成视觉闪动。我在所有图表里都加了animation: false或者只在首次渲染时开动画后续更新关闭动画。第二个坑是图表容器在页面加载时宽度为0导致图表渲染出来是压缩的。尤其是用了v-if控制图表显隐的场景更容易触发。解决办法是在nextTick后调用init同时监听窗口resize事件重绘图表。nextTick(() { const chart echarts.init(document.getElementById(yearlyChart)); chart.setOption(option); }); window.addEventListener(resize, () chart.resize());第三个坑是数据格式化。票房值的数量级差别很大如果直接展示原始数值图表Y轴刻度会非常稀疏看起来不专业。我统一封装了一个formatMoney函数把票房转成1.2亿3500万这样的可读字符串。7. 项目落地运行流程、测试与论文配合7.1 从源码到跑通环境配置一定要写清楚毕设源码必须附带一份清晰的环境配置说明这是我踩过坑后的血泪教训。当时我为了图省事README里只写了一句参考requirements.txt结果指导老师在自己电脑上一跑就报错最后花了半天陪他排查环境问题。后来我把环境配置部分重写成了完整的步骤从创建虚拟环境、安装依赖、初始化数据库到启动后端、启动前端每一步都配上验证方法。# 1. 创建虚拟环境 python -m venv venv # 2. 激活虚拟环境Windows venv\Scripts\activate # 3. 安装依赖 pip install -r requirements.txt # 4. 初始化数据库 mysql -u root -p sql/init.sql python scripts/init_data.py # 5. 启动后端 python app.py # 6. 启动前端新终端 cd frontend npm install npm run dev这里有个非常容易被忽略的点数据库初始化和依赖安装顺序。我建议把sql/init.sql单独放在sql目录而不是混在代码里因为指导老师通常需要先看到表结构他才会放心让你把数据导进去。7.2 测试用例设计让系统能跑变成系统经得起验证很多同学写完毕设系统演示能跑就结束了但测试部分的缺失会让论文里的系统测试一章非常单薄。我的做法是设计了功能测试、接口测试和异常测试三类用例。接口测试用Postman做了20多个请求覆盖8个后端接口的正常、边界和异常三种情况。比如在票房查询接口中测试了不传日期参数传非法日期格式传未来日期三种异常输入确保系统返回的错误信息友好且不会崩溃。这个不复杂但答辩时展示了异常测试的日志老师明显感觉到项目的完整度比单纯演示多了不少。7.3 论文章节怎么和项目对应毕设答辩看得最多的除了代码就是论文。我的论文结构是按照项目的生命周期组织的每一章都能对应一个实际交付物第一章绪论写选题背景、国内外研究现状这部分要多引用真实行业报告。第二章相关技术介绍Python、Flask、MySQL、ECharts、爬虫框架。第三章需求分析直接沿用我前面功能模块拆解的表。第四章系统设计写架构图和数据库设计附完整建表SQL。第五章系统实现按模块贴核心代码和运行截图。第六章测试贴接口测试和功能测试用例。这样安排的好处是论文不是写完代码之后硬编的而是项目开发过程中自然积累的文档。我每完成一个模块就顺手填充论文对应章节的初稿最后集中改一轮格式就差不多了。8. 写在最后给打算做类似毕设的人几句实话做这个电影市场分析系统前前后后花了大概两个月。第一个月主要在爬数据、清洗数据、跟网站接口斗智斗勇第二个月做分析模块、前端可视化和写论文。回头复盘有三点体会特别深刻。第一千万别想着一步到位。第一版爬虫我只爬了100部电影先把整个流程跑通让数据落库、接口打通、图表能出图然后再扩量爬到500部以上。先有一个能用的简化版比憋一个大而全的完美版强一百倍。第二日志记录要养成习惯。无论是爬虫日志还是后端接口日志都写好输出格式和时间戳。调试时靠日志定位问题写论文时靠日志说明系统可靠性答辩演示时也可以现场展示日志内容。第三博文里提到的源码包不是我临时整理的。我从项目第一天就用Git管理代码每个模块的提交记录都清晰可见。答辩评审老师如果问到这个模块改了哪些版本我可以直接调出提交历史这种细节比口头空谈有说服力得多。我的这套电影市场分析系统已经跑通并完成答辩如果你也想做一个类似的数据分析毕设项目可以直接参考我前面写的这几章内容和源码结构从选题、爬虫到可视化按照顺序一步步来不会太难的。最后给你一个小建议动手做之前先花一个周末把需求拆解表写出来后面代码的顺畅程度会远超你的想象。
返回列表