春节档电影数据爬虫与可视化分析毕业设计全攻略
简介这份毕业设计项目包基于Python实现春节档电影信息的爬取与数据可视化分析面向计算机、数学、电子信息等专业的本科及高职学生适用于课程设计、期末大作业或毕业设计参考。项目覆盖从数据采集、清洗入库、统计整理到可视化展示的完整闭环核心代码、数据库及前端页面均已齐备并附有答辩PPT和说明文档便于理解整体设计思路后进行二次开发与功能扩展。压缩包共39个文件主要含Python源码、HTML/CSS/JavaScript页面资源、数据库文件、配置文件、启动脚本、图片与PDF文档等整体大小20.86MB目录结构简洁按功能分模块存放可快速定位所需材料。目前已有1094人学习下载无论是完成课程任务还是准备毕业答辩都能从中获得从技术选型到结果呈现的完整参考。1. 春节电影爬虫为什么这个毕业设计年年有人做每年春节档结束猫眼、豆瓣、灯塔上会沉淀出一批类型丰富、时间跨度集中的电影数据票房、评分、想看人数、排片占比、上映天数全都有而且字段结构还算规整——这正是练手 Python 爬虫和数据可视化最顺手的数据集。这个课题的完整形态是一套“数据采集 清洗落库 可视化分析 答辩 PPT”的全链路项目适合正处于毕业设计选型期的计算机相关专业学生也适合想快速验证爬虫工程化能力的一线开发。很多人以为难点在爬虫实际做过就知道反爬策略、数据口径统一和图表叙事才是真正拉开差距的地方。本文按这套源码常见的技术路线把从数据源选型到答辩演示的完整方案讲透。2. 春节档电影数据源选型与反爬策略2.1 猫眼、豆瓣、灯塔三选一数据侧重点完全不同做春节档电影数据可视化第一步不是写代码而是先明确要分析什么问题。不同数据源可以回答的问题不同后端存储结构也就不同。常见做法是三选一为主、另一个为辅避免一来就并行爬三个站导致字段口径对不上。数据源核心字段适合分析方向爬取难度猫眼专业版实时票房、排片占比、上座率档期大盘趋势、单片票房走势中有 JS 动态渲染接口豆瓣电影评分、评价人数、短评内容口碑分析、观众情绪倾向中低静态页面为主灯塔专业版预售票房、想看人数、受众画像映前热度预测、宣发效果高字段加密较多对于毕业设计来说最稳妥的组合是豆瓣电影做评分和短评分析猫眼做票房和排片趋势分析。两者都在春节档这个时间窗口内保持每日更新数据连续性有保障。如果你所在学校对数据来源有明确要求优先选豆瓣它的页面结构相对稳定反爬强度可控爬下来的短评还能顺带做词云可视化素材更多。2.2 动态页面与接口优先策略把 BS4 用在正确位置热词里频繁出现“bs4 爬取动态页面”很多同学会拿 BS4 直接解析整个 HTML 页面遇到内容是 JS 动态加载的就抓瞎。实际上BS4 适合解析服务端渲染后的静态 HTML而猫眼的票房数据是通过 XHR 异步请求返回的 JSON 数据浏览器里看到的标签是渲染后的结果。正确做法是先打开浏览器开发者工具的 Network 面板找返回 JSON 的接口地址直接请求这个接口再用json模块解析性能远高于渲染完整页面。如果遇到接口参数加密退路才是 Selenium 模拟浏览器操作。但 Selenium 启动慢、容易被识别不建议作为主力方案。我一般这样排序先找公开 JSON 接口其次用 requests 请求静态页面配合 BS4 解析最后才用 Selenium 兜底。顺序对了爬虫代码的量级会差出好几倍。3. 用 Python 把春节档电影信息稳定落库3.1 定义数据表结构与字段规范爬虫代码写得好不好看表结构就能判断。春节档电影信息至少要分成两张表电影基础信息表和每日票房快照表。基础表存电影 ID、片名、导演、主演、类型、上映日期、片长快照表存电影 ID、统计日期、票房、排片场次、上座率。快照表按天追加这样后期才能画出时间序列曲线。CREATE TABLE movie_info ( movie_id TEXT PRIMARY KEY, title TEXT NOT NULL, directors TEXT, actors TEXT, genres TEXT, release_date DATE, duration INTEGER ); CREATE TABLE daily_box_office ( id INTEGER PRIMARY KEY AUTOINCREMENT, movie_id TEXT NOT NULL, stat_date DATE NOT NULL, box_office REAL, show_count INTEGER, seat_count INTEGER, avg_seat_rate REAL, UNIQUE(movie_id, stat_date) );这里把movie_id设计成字符串而不是自增整数是因为外部数据源有自己的影片 ID直接用源 ID 做主键可以方便后续增量更新和跨表关联。快照表加了联合唯一约束movie_id stat_date重复写入时会报错正好用来做去重。3.2 手写一个最小可用的动态接口爬虫脚本以猫眼专业版春节档榜单为例核心步骤是构造请求头、拿 JSON、抽取字段、写入 SQLite。下面给一个能直接跑通流程的最小脚本骨架import json import sqlite3 import requests from datetime import date headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://piaofang.maoyan.com/ } def fetch_spring_festival_data(target_date: str) - list: 拉取指定日期的春节档票房榜单返回电影字段列表 url https://piaofang.maoyan.com/boxoffice/rank.json params { date: target_date, type: day } resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() payload resp.json() movies [] for item in payload.get(data, {}).get(list, []): movies.append({ movie_id: item[movieId], title: item[movieName], box_office: float(item[boxInfo].replace(万, )), show_count: int(item[showInfo].split(场)[0]), stat_date: target_date }) return movies def save_to_db(movies: list) - None: conn sqlite3.connect(spring_festival.db) cursor conn.cursor() for m in movies: cursor.execute( INSERT OR IGNORE INTO daily_box_office (movie_id, stat_date, box_office, show_count) VALUES (?, ?, ?, ?), (m[movie_id], m[stat_date], m[box_office], m[show_count]) ) conn.commit() conn.close() if __name__ __main__: data fetch_spring_festival_data(date.today().isoformat()) save_to_db(data) print(f今日写入 {len(data)} 条记录)逻辑说明fetch_spring_festival_data里先拼请求参数timeout10防止网络抖动时进程卡死resp.raise_for_status()在 HTTP 状态码异常时直接抛错方便日志定位。save_to_db里用了INSERT OR IGNORE利用表上的联合唯一约束跳过重复数据。参数说明typeday表示按日维度取数如果改成typeweek可以得到周榜boxInfo返回的是带“万”的字符串必须转 float 再存否则后续聚合没法算。3.3 断点续爬与异常重试的工程化处理上面是单日抓取毕业设计要求的是一整个春节档周期跨 7 到 15 天中间必须考虑失败重试和断点续爬。常见做法是维护一个待爬日期队列已经成功的日期从队列里移除失败的任务最多重试三次重试间隔指数退避。import time from datetime import timedelta def crawl_date_range(start: date, end: date) - None: current start while current end: for attempt in range(3): try: movies fetch_spring_festival_data(current.isoformat()) save_to_db(movies) break except Exception as exc: print(f{current} 第 {attempt 1} 次失败: {exc}) time.sleep(2 ** attempt) else: print(f{current} 重试 3 次仍失败跳过稍后可手动补采) current timedelta(days1)这里用for...else的写法循环内break代表成功else块只有在三次尝试都异常时才会执行正好标记需要补采的日期。重试间隔依次是 1 秒、2 秒、4 秒比固定间隔更不容易触发服务端的访问频率限制。实际采集时建议每次请求后额外time.sleep(1)左右把 QPS 控制在个位数这是业界默认的礼貌爬取规范。4. 数据清洗与指标体系从票房数字到可分析维度4.1 清洗脏数据的三个高频场景爬虫拿到的数据不能直接用。我整理过一份春节档电影数据脏数据主要集中在三种情况票房单位不统一有的返回“万”有的返“亿”同一部影片在不同日期被 ID 不同地录入演员和类型字段夹带空白字符或 HTML 标签残留。清洗代码里需要重点处理这三件事。import pandas as pd def clean_box_office(value) - float: 将 1.2亿 / 3456万 / 纯数字 统一转成以 万 为单位的浮点数 if isinstance(value, (int, float)): return float(value) / 10000 text str(value).replace(,, ).strip() if text.endswith(亿): return float(text[:-1]) * 10000 if text.endswith(万): return float(text[:-1]) return float(text) def clean_movie_frame(raw_df: pd.DataFrame) - pd.DataFrame: df raw_df.copy() df[box_office] df[box_office].apply(clean_box_office) df[title] df[title].str.strip() df[genres] df[genres].str.replace(r[^], , regexTrue) df[stat_date] pd.to_datetime(df[stat_date]) df df.drop_duplicates(subset[movie_id, stat_date], keeplast) return df代码逻辑clean_box_office先判断类型再按字符串结尾字符选择换算逻辑所有金额统一成“万”才能做数值运算。clean_movie_frame里先去掉片名首尾空格再正则剔除 HTML 标签残留最后按影片加日期去重。参数说明keeplast表示同一天同一部影片重复录入时保留最后一次写入的版本适用于接口重试导致的数据覆盖场景。4.2 春节档专有的指标体系通用的电影分析指标有票房、排片比、上座率但春节档还有两个特殊维度一个是档期累计票房另一个是“大年初一开画成绩”。这两个指标的代码实现很简单但承载的分析价值完全不同。指标计算方式分析用途单片档期累计票房groupby(movie_id).box_office.sum()排名与头部集中度单片每日票房环比groupby(movie_id).box_office.pct_change()口碑扩散趋势排片占比show_count / 当日总场次影院资源调配方向上座率avg_seat_rate直接取接口字段供需关系判断档期票房集中度前 3 名票房合计 / 档期总票房市场垄断程度特别要注意的是“排片占比”必须按日计算因为春节档每天总场次不同直接用绝对场次跨日对比没有意义。上座率字段在部分接口里是缺省的缺失率超过 30% 就要考虑放弃整列或者用“票房除以场次”的场均产出替代。4.3 用 pandas 做一版含金量足够的分析清洗之后的分析不需要太花哨能覆盖趋势、对比、结构三个维度就足以上答辩台面。趋势看每日累计票房曲线对比看同档期几部头部影片的票房走势结构看各类型影片的票房占比。下面这段代码一次完成三类计算# 假设 df 已经过 clean_movie_frame 清洗 daily_total df.groupby(stat_date)[box_office].sum() movie_daily df.pivot_table( indexstat_date, columnstitle, valuesbox_office, aggfuncsum ) daily_total daily_total.sort_index() movie_daily movie_daily.fillna(0) # 档期票房集中度 top3 movie_daily.sum().nlargest(3).sum() total movie_daily.sum().sum() concentration top3 / total if total 0 else 0 print(f档期前3集中度: {concentration:.1%})pivot_table的关键是把“长表”转成“宽表”行是日期、列是影片名这样后续画折线图时每部影片就是一条完整的序列。nlargest(3)取累计票房最高的三部影片。集中度超过 60% 说明今年春节档头部效应明显这个数可以作为答辩 PPT 上的核心论点。5. 用 pyecharts 做春节档电影可视化分析5.1 为什么选择 pyecharts 而非原生 Matplotlib毕业设计答辩时图表的美观程度直接影响评委的第一印象。Matplotlib 默认样式太理工科需要额外配字体和主题才好看pyecharts 生成的是基于 ECharts 的 HTML 文件交互式图表自带鼠标悬浮、缩放、图例切换功能Python 代码写完直接输出 HTML放 PPT 里演示时观感接近商业产品。对于春节档电影数据最常见的组合是折线图展示每日票房趋势、柱状图展示总票房排行、饼图展示类型占比、词云展示短评关键词。5.2 票房趋势与排片对比的核心图表代码按照前面清洗好的数据直接喂给 pyecharts。下面代码生成一个双系列折线图同时展示档期总票房和当日票房环比from pyecharts.charts import Line, Bar from pyecharts import options as opts def make_trend_chart(daily_total: pd.Series) - Line: line ( Line(init_optsopts.InitOpts(width1200px, height600px)) .add_xaxis(daily_total.index.strftime(%m-%d).tolist()) .add_yaxis( 每日票房(万), daily_total.round(0).tolist(), is_smoothTrue, label_optsopts.LabelOpts(is_showFalse) ) .set_global_opts( title_optsopts.TitleOpts(title春节档每日票房趋势), tooltip_optsopts.TooltipOpts(triggeraxis), yaxis_optsopts.AxisOpts(name票房(万)) ) ) return line line make_trend_chart(daily_total) line.render(box_office_trend.html)add_yaxis里is_smoothTrue让折线更圆滑label_opts关掉数值标签防止密集数据点文字重叠。triggeraxis保证鼠标悬浮时同一天的所有系列数值一起展示。render输出一个独立 HTML 文件双击就能在浏览器里看效果不需要启动任何服务。5.3 短评词云与评分分布的组合呈现票房之外短评词云是春节档项目里最容易出效果、也最容易翻车的部分。翻车点在于中文分词。直接用英文空格分词会把“流浪地球”切成“流浪”、“地球”两个词必须引入 jieba 先做分词再过滤停用词。一个能用的词云绘制流程是这样import jieba from wordcloud import WordCloud def build_wordcloud(comments: list, output_path: str) - None: text .join(comments) words jieba.cut(text) filtered [w for w in words if len(w) 1 and w not in STOPWORDS] wc WordCloud( font_pathC:/Windows/Fonts/msyh.ttc, width1000, height700, background_colorwhite ).generate( .join(filtered)) wc.to_file(output_path)font_path必须指定中文字体否则词云图片里全是乱码方块Windows 上一般用微软雅黑msyh.ttcMac 上换成/System/Library/Fonts/PingFang.ttc。len(w) 1过滤单字STOPWORDS里装“电影”“一部”“真的”这类无信息量词。词云图片生成后再拿 PIL 或 matplotlib 贴到画布上配到 PPT 里就是一张能讲故事的热度图谱。评分分布则是直方图直接用 pyecharts 的 Bar 组件把“9 分以上 / 8 到 9 分 / 7 到 8 分”分桶统计比散点图直观得多。5.4 可视化页面的布局技巧毕业设计的可视化部分不应只是一堆孤立图表要按分析逻辑排顺序。常见布局是先放档期总览总票房数字卡片 每日票房折线再放单片对比头部影片票房走势接着放结构分析类型饼图 评分分布最后放词云收尾。每个 HTML 图表之间保持统一的配色主题pyecharts 里可以通过InitOpts(themedark)或light全局统一不要让一份 PPT 里出现三套风格。图表文件名按照01_box_office_trend.html的顺序命名答辩演示时直接按文件名顺序打开。6. 答辩演示前必调的 3 个细节6.1 先验证数据完整性再开 PPT答辩演示最常见的事故是评委问“这个数据是全的吗”答不上来。提前跑一段自检脚本把爬取日期范围、每天影片数量、字段缺失率一次算清conn sqlite3.connect(spring_festival.db) df pd.read_sql_query(SELECT * FROM daily_box_office, conn) missing_rate df[box_office].isna().mean() date_range f{df[stat_date].min()} 至 {df[stat_date].max()} print(f覆盖日期: {date_range}) print(f总记录数: {len(df)}) print(f票房缺失率: {missing_rate:.1%})这组数字建议直接放进答辩 PPT 的附录页证明数据采集过程是可靠的。缺失率超过 10% 就说明某个数据源在春节档期间有过连续失败需要先补采再答辩。6.2 图表里的数量单位统一标在轴名称里猫眼接口给的值有“万”有“亿”清洗时统一成万之后图表标题和 y 轴名称也要同步写清楚否则评委看到几千这个数会当场质疑。pyecharts 里AxisOpts(name票房(万))就是干这个的做好了能避免一半的口头解释。同一份 PPT 里所有涉及金额的图表必须用同一单位。上座率用百分比评分用 0 到 10 分口径统一图表之间才能互相参照。6.3 把分析结论做成图表旁的注释文字图表自己不会说话评委看的是你能否从图里提炼结论。比较稳妥的做法是每张图上加一句注释文字比如“前 3 名影片贡献了档期 60% 以上的票房头部效应显著”这句话无论是放在 PPT 的图表下方还是代码里的TitleOptssubtitle 位置都能让人一眼看到分析重点。答辩 PPT 的结构建议按“数据采集说明 → 清洗过程 → 指标体系 → 可视化图表 → 分析结论 → 不足与展望”排每一页只讲一件事图表旁边必须有结论句不要放高密度表格。源码目录里爬虫、清洗、可视化三个模块拆开建文件夹main.py 放顶层能让评委快速定位结构。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →