ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python电影数据分析毕设实战:从爬取到可视化全流程

Python电影数据分析毕设实战:从爬取到可视化全流程

简介:这份资源是面向计算机相关专业学生与项目实战学习者的Python电影数据分析及可视化毕业设计完整包,经导师指导并认可,可直接用于毕设、课程设计或期末大作业。项目围绕电影数据的采集、清洗、统计与图表呈现展开,涵盖数据存储、后端逻辑与前端页面展示等环节,适合需要完整案例练手或快速搭建答辩作品的同学。压缩包共39个文件,约20.87MB,包含Python源码、数据库文件、网页模板与静态资源、答辩PPT及说明文档等,结构清晰,便于按模块阅读与二次修改。目前已有468人学习下载,说明该案例具备一定参考价值。读者可获得可运行的完整项目源码、配套答辩演示材料与项目说明,并借助已调试通过的代码理解数据分析到可视化的实现路径,减少从零搭建的时间成本。

1. 电影数据分析毕设包:从爬取到可视化的完整落地路径

很多同学做毕业设计时,选题定的是"电影数据分析",结果卡在第一步——数据从哪来。网上找的 CSV 要么字段残缺,要么只有几十条,跑出来的图表连自己都不信。这个基于 Python 的电影数据分析及可视化项目包,解决的正是这个问题:它把爬取、清洗、入库、可视化、答辩 PPT 串成了一条完整链路,拿到手就能跑,跑完就能改,改完就能答辩。

项目主体是main.py配合DataBase.py,用 Flask 搭了一个轻量 Web 界面,数据落在data.db里,模板在templates目录,静态资源在static。附带MovieName.txt作为爬取种子,run.bat一键启动,还有答辩 PPT 和几份参考 PDF。适合计算机相关专业正在做毕设的学生,也适合想拿一个完整数据分析项目练手的人。下面我按"能跑起来 → 能看懂 → 能改 → 不翻车"的顺序拆一遍。

2. 环境搭建与项目启动:把 main.py 跑通的最小闭环

2.1 依赖判断与 Python 版本选择

这个项目没有附带requirements.txt,这是第一个需要自己补的地方。从代码结构看,它用到了 Flask(Web 框架)、requests 或类似的爬虫库、sqlite3(标准库)、以及前端可视化大概率是 ECharts 或 Pyecharts。我一般会先建虚拟环境再逐个装,避免污染全局。

Python 版本建议 3.8 到 3.10。3.11 以上部分库的 wheel 可能还没跟上,尤其是涉及 pandas 老版本的时候。如果你机器上已经有 3.9,直接用就行,不用折腾。

# 创建虚拟环境,名字随意,这里用 venv python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 装核心依赖,版本不锁死,先跑通再说 pip install flask requests pandas pyecharts

这里解释一下为什么这么装:Flask 是 Web 入口,requests 负责爬取,pandas 做数据清洗和统计,pyecharts 或前端 ECharts 负责出图。如果你跑起来报ModuleNotFoundError,缺什么补什么,不用一次性全装。

提示:如果 pip 下载慢,换国内镜像源,命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple,这是常规操作,不算翻车。

2.2 数据库初始化与 run.bat 启动逻辑

项目里已经有data.db,说明作者已经把爬好的数据打包进去了。你拿到手第一件事不是重新爬,而是先确认这个库能不能读。用 sqlite3 命令行或者 DB Browser for SQLite 打开看一眼表结构。

# 查看数据库里有哪些表 sqlite3 data.db ".tables" # 看某张表的字段结构,假设表名叫 movie sqlite3 data.db ".schema movie" # 快速看前 5 条数据 sqlite3 data.db "SELECT * FROM movie LIMIT 5;"

如果data.db里的数据完整,你其实可以跳过爬取阶段,直接进入分析和可视化。run.bat的内容大概率就是python main.py,Windows 下双击就能启动 Flask 服务。启动后浏览器访问http://127.0.0.1:5000,能看到页面就说明链路通了。

main.py里通常定义了几个路由:首页展示图表、/api/data返回 JSON 数据、可能还有一个/refresh触发重新爬取。你打开main.py先看@app.route装饰器有几个,就知道这个项目对外暴露了哪些功能。

2.3 爬取模块的触发条件与 MovieName.txt 的作用

MovieName.txt是爬取种子文件,里面大概率是一行一个电影名。爬虫逻辑一般是:读这个文件 → 逐个请求目标页面 → 解析字段 → 写入data.db。如果你要重新爬,先确认这个文件里的电影名还有效,目标网站结构没变。

# 伪代码示意,实际以项目里的爬取函数为准 import requests from bs4 import BeautifulSoup def fetch_movie(name): # 构造搜索请求,具体 URL 以项目代码为准 url = f"https://example.com/search?q={name}" headers = {"User-Agent": "Mozilla/5.0"} resp = requests.get(url, headers=headers, timeout=10) soup = BeautifulSoup(resp.text, "html.parser") # 解析标题、评分、年份等字段 # 返回字典 return {"name": name, "score": None}

这段代码的关键参数是headers和timeout。不加 User-Agent 很多站点直接拒绝,不加 timeout 遇到慢响应会卡死。实际项目里可能用的是正则或者 xpath,逻辑一样。爬完记得检查app.log,里面记录了运行日志,报错信息都在那。

3. 数据清洗与字段映射:从原始表到可视化可用结构

3.1 字段类型转换与缺失值处理

爬下来的数据大概率是字符串格式,评分是"8.5"而不是8.5,年份可能带"年"字。直接拿去画图,坐标轴会乱。常见做法是在DataBase.py里加一层清洗函数,或者在main.py读取后做转换。

import pandas as pd import sqlite3 conn = sqlite3.connect("data.db") df = pd.read_sql("SELECT * FROM movie", conn) # 评分转浮点,转换失败的置为 NaN df["score"] = pd.to_numeric(df["score"], errors="coerce") # 年份提取数字,假设格式是 "2023年" 或 "2023" df["year"] = df["year"].astype(str).str.extract(r"(\d{4})") df["year"] = pd.to_numeric(df["year"], errors="coerce") # 丢掉评分为空的行,或者用均值填充,看你的分析目标 df_clean = df.dropna(subset=["score", "year"]) # 按年份统计平均评分 yearly = df_clean.groupby("year")["score"].mean().reset_index() print(yearly.head())

errors="coerce"的作用是转换失败不报错,直接给 NaN,方便后续统一处理。str.extract用正则把四位数字抠出来,比直接replace稳。dropna的subset参数指定只看哪几列,避免误删有效数据。

3.2 可视化接口的数据格式对齐

前端 ECharts 通常要{categories: [...], values: [...]}这种结构。后端从 DataFrame 转成 JSON 时,字段名要对上。如果你改了数据库字段名,前端 JS 里的dataIndex也要跟着改,否则图表空白。

# 假设 Flask 路由返回 JSON from flask import jsonify @app.route("/api/yearly_score") def yearly_score(): data = yearly.to_dict(orient="records") categories = [str(int(row["year"])) for row in data] values = [round(row["score"], 2) for row in data] return jsonify({"categories": categories, "values": values})

orient="records"把 DataFrame 转成列表套字典,每行一个字典。round保留两位小数,避免前端显示一长串。返回的 key 名categories和values必须和前端option里写的一致,这是最容易翻车的地方——后端改了名,前端没改,页面一片空白,控制台也不报错。

注意:如果你用的是 Pyecharts 直接在后端渲染,就不需要这个 JSON 接口,直接在 Python 里生成 HTML 返回。两种方式选一种,别混用。

3.3 数据库表结构变更后的同步问题

如果你重新爬取并改了表结构,比如加了一个director字段,记得同步更新三处:DataBase.py里的建表语句、main.py里的查询语句、前端模板里的展示逻辑。漏掉任何一处,要么报no such column,要么页面显示undefined。

-- 查看当前表结构,确认字段名 PRAGMA table_info(movie); -- 如果需要加字段 ALTER TABLE movie ADD COLUMN director TEXT;

PRAGMA table_info是 SQLite 查看表结构的标准方式,比.schema更简洁。加字段用ALTER TABLE,但注意 SQLite 不支持删除字段,改结构前最好备份data.db。

4. 可视化图表配置与前端联调:ECharts 参数怎么改

4.1 柱状图与折线图的 option 配置

项目里的图表大概率是 ECharts 渲染的,模板文件在templates目录下。打开 HTML 找到echarts.init那段,核心是option对象。以年份平均评分折线图为例:

var chart = echarts.init(document.getElementById('yearlyChart')); var option = { title: { text: '各年份平均评分' }, tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: categories, // 后端传来的年份数组 axisLabel: { rotate: 45 } // 年份多的时候倾斜,防止重叠 }, yAxis: { type: 'value', min: 0, max: 10 }, series: [{ data: values, type: 'line', smooth: true, markLine: { data: [{ type: 'average', name: '平均值' }] } }] }; chart.setOption(option);

axisLabel.rotate是解决横坐标太密集的标准做法,热搜里也有人问"python画图横坐标太密集",前端 ECharts 用这个参数,Matplotlib 用plt.xticks(rotation=45)。markLine加一条平均线,答辩时老师一眼就能看出哪些年份高于均值,比干巴巴的折线有说服力。

4.2 前后端数据对接的调试方法

联调时最常见的现象是图表不显示。排查顺序:先打开浏览器 F12 看 Network 里/api/yearly_score返回了什么,如果是 500 说明后端报错,去看app.log;如果返回了 JSON 但图表空白,看 Console 有没有 JS 报错;如果都没报错,检查echarts.init的 DOM 容器有没有宽高——容器高度为 0 是经典坑。

/* 确保图表容器有明确高度 */ #yearlyChart { width: 100%; height: 400px; }

ECharts 要求容器必须有宽高,用百分比高度时父元素也要有高度,否则渲染出来是空白。我一般直接给固定像素高度,省事。

4.3 多图表页面的布局与响应式处理

答辩演示时可能要在不同分辨率屏幕上展示,图表布局用 flex 或 grid 比绝对定位稳。项目里如果有多个图表,建议每个图表单独一个div,用 CSS Grid 两列排布,小屏幕自动变一列。

.chart-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 20px; padding: 20px; }

auto-fit配合minmax是响应式网格的常用写法,屏幕宽就两列,窄就一列,不用写媒体查询。每个图表容器放进 grid 里,高度统一设 400px,页面看起来整齐。

5. 避坑与常见问题排查:答辩前必须过一遍的检查项

5.1 启动报 ModuleNotFoundError

现象:双击run.bat或执行python main.py后直接报缺模块。原因:项目没带requirements.txt,依赖没装全。解决:根据报错信息逐个pip install,或者直接装一套常用的:pip install flask requests pandas pyecharts beautifulsoup4 lxml。装完再跑,还缺就继续补。

5.2 数据库字段与代码不匹配

现象:页面能打开但图表空白,app.log里报no such column: xxx。原因:你重新爬取后改了表结构,但查询语句还是旧字段名。解决:用PRAGMA table_info(movie)看实际字段,把main.py和DataBase.py里的 SQL 语句同步改掉。改完重启 Flask 服务。

5.3 爬取被目标站点拒绝

现象:重新爬取时app.log里出现 403 或 429,数据写不进去。原因:请求频率太高或者没带 User-Agent。解决:在请求头里加User-Agent,每次请求之间time.sleep(1)到time.sleep(3),别把人家站点打挂。如果目标站点结构变了,解析规则也要跟着调,这个没有通用解,只能对着页面重新写。

5.4 图表在答辩电脑上显示异常

现象:自己电脑上好好的,拷到答辩教室的电脑上图表错位或者不显示。原因:分辨率不同、浏览器版本不同、或者 ECharts 的 CDN 资源没加载出来。解决:把 ECharts 的 JS 文件下载到static目录本地引用,别用 CDN。容器高度用固定像素,别用vh。提前在答辩电脑上试一遍,这是血泪经验。

5.5 PPT 与代码版本不一致

现象:PPT 里写的功能代码里找不到,或者数据对不上。原因:PPT 是早期版本,代码后来改了但没同步。解决:答辩前把 PPT 里的每个图表截图和当前代码跑出来的结果对一遍,不一致就以代码为准改 PPT。别让老师问"你这个图怎么和演示的不一样"。

6. 从能跑到能答辩:二次开发与验证技巧

项目跑通只是及格线,答辩要拿高分得有自己的东西。最省力的二次开发方向是换数据源或加分析维度。比如把MovieName.txt换成你感兴趣的导演作品列表,爬下来做导演风格分析;或者在现有评分、年份基础上加一个"类型"字段,做类型与评分的交叉分析。

验证数据是否可靠有个笨办法但很管用:手动抽 5 条记录,去目标网站核对评分和年份,如果对得上,说明爬取和清洗逻辑没问题。对不上就回去查解析规则。这个步骤我每次做完爬虫都会走一遍,花不了十分钟,但能避免答辩时被问住。

# 抽样核对,假设从数据库读 sample = df.sample(5) for _, row in sample.iterrows(): print(f"电影:{row['name']},评分:{row['score']},年份:{row['year']}") # 然后手动去网站搜这几部电影,对比数值

另一个技巧是把app.log的日志级别调成 DEBUG,跑一遍完整流程,看每一步的耗时和输出。如果某个环节特别慢,比如爬取 100 部电影花了 10 分钟,答辩演示时肯定来不及,提前把数据爬好存库,演示时直接读库。

最后说个习惯:每次改完代码,先本地跑一遍完整流程——启动服务、打开页面、点每个图表、看日志有没有报错——再提交或打包。从那以后我每次交付项目前都强制走一遍这个检查清单,省得在答辩现场翻车。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进