尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于Python的电影数据可视化分析系统:从爬虫采集到Web展示全流程

发布时间:2026/9/24 22:37:31

资讯中心
01
ARTICLE

基于Python的电影数据可视化分析系统:从爬虫采集到Web展示全流程

基于Python的电影数据可视化分析系统:从爬虫采集到Web展示全流程
简介面向毕业设计场景的电影数据可视化分析系统源码包适合计算机相关专业学生用于课程设计、毕设参考或数据分析实践。项目将豆瓣电影数据爬取后存入SQLite基于Flask搭建后端结合ECharts、Bootstrap、WordCloud完成前端可视化展示覆盖数据采集、存储、接口开发与图表呈现的完整流程。压缩包共2000个文件以1684个Python脚本为核心辅以124个HTML页面、93个JavaScript交互脚本、18个CSS样式文件另有少量TXT数据说明、PDF文档与Markdown笔记整体体积约76.51MB目录结构清晰便于按模块阅读。源码经严格调试评审得分95分以上具备开箱即用的可运行性并已吸引1018人学习下载。下载后可获得完整项目源码、数据文件及页面模板既能直接运行观察可视化效果也可参考其爬虫、Flask路由与ECharts配置方式为毕业设计或数据分析项目提供可复用的实现思路。1. 这个毕业设计到底在做什么不是画图是把“电影数据”变成能答辩的完整闭环先说一个可能反直觉的结论这类“基于Python的电影数据可视化分析系统”真正让你熬夜的通常不是画图而是“数据从哪里来、怎么洗干净、用什么口径去分析”。如果你只是从网上下一个 csv 再画几张柱状图那它叫“作业”不叫“系统”毕业设计要的是一个能自圆其说的闭环——从数据采集、清洗、存储到指标计算和可视化展示每一步都有你的设计理由。这个标题对应的其实是一套非常标准的“数据分析 可视化”全流程项目常见做法是用 Python 做数据处理pandas、numpy把豆瓣、TMDB、猫眼等平台的电影数据抓取或整理成结构化表格然后通过图表展示票房、评分、类型分布、年份趋势等维度的规律。适合的人群很明确计算机、大数据、信息管理相关专业的本科生或研究生尤其是需要快速搭建一个演示性强、有真实数据支撑、能讲清楚“我做了什么”的毕设项目。接下来我按自己做这类项目的习惯把整条链路拆开讲数据怎么来、怎么洗、怎么存、怎么分析、怎么可视化以及最后怎么封装成一个能演示的系统。你按这个顺序做每一步都有对应的代码和参数可以抄遇到问题也有地方查。2. 先用 Python 把电影数据“盘活”抓取、解析与本地化存储2.1 数据获取的三个来源爬虫采集、公开数据集、手工整理做电影数据可视化第一步不是写代码而是确定数据源。我接触过的毕设项目里最常见的数据来源有三个爬虫采集比如豆瓣 Top250、猫眼票房榜、公开数据集Kaggle 的 TMDB 5000 Movie Dataset、IMDb 子集、以及导师或课程提供的 csv 文件。三者各有适用场景。如果你想要“工作量足、有技术含量、答辩有故事可讲”爬虫是首选它天然覆盖网络请求、HTML 解析、反爬应对、异常处理这些点如果时间紧或者评委更看重分析逻辑公开数据集更省事但你需要额外说明数据来源和口径。我一般建议是主数据用公开数据集打底再写一个小爬虫补充实时数据或特定榜单这样既有稳定性又有亮点。不要一上来就全量爬容易被反爬机制卡住后面会专门讲。2.2 用 requests BeautifulSoup 抓取豆瓣 Top250最小可运行代码以豆瓣电影 Top250 为例这个页面结构稳定、数据字段完整片名、评分、评价人数、经典台词、年份、地区、类型是毕业设计出镜率最高的目标。这段代码是当时我调试好留存的版本你可以直接在 PyCharm 里跑。import requests from bs4 import BeautifulSoup import time import csv headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } all_movies [] for page in range(10): # Top250 共10页每页25条 url fhttps://movie.douban.com/top250?start{page * 25}filter resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) for item in soup.select(.item): title item.select_one(.title).get_text() rating item.select_one(.rating_num).get_text() quote item.select_one(.quote span) quote quote.get_text() if quote else # 提取年份/地区/类型示例1994 / 美国 / 犯罪 剧情 info item.select_one(.bd p).get_text().strip().split(\n)[1].strip() all_movies.append([title, rating, quote, info]) time.sleep(2) # 适度休眠降低请求频率 with open(douban_top250.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([title, rating, quote, info]) writer.writerows(all_movies) print(f抓取完成共 {len(all_movies)} 条数据)这段代码的逻辑很直白循环请求10页解析每页的.item节点取出标题、评分、台词和元信息最后写入 csv。这里有两个参数你务必理解不要照抄了事。第一个是headers里面必须带 User-Agent豆瓣会拒绝裸请求返回 418第二个是encodingutf-8-sig这个编码会在文件头写入 BOM用 Excel 打开 csv 时中文不乱码如果你后续用 pandas 读取建议改成utf-8避免字段名带\ufeff前缀。2.3 解析字段时的常见问题评分是字符串年份藏在段落里很多人在第一次跑这种代码时拿到的 csv 打开看是“对”的但放进数据分析阶段才发现问题。比如info字段是1994 / 美国 / 犯罪 剧情评分是9.7这种字符串评价人数在另一个节点里还没被取出来。这就引出一个原则采集阶段多取原始字段清洗阶段再做转换。我的习惯是每个字段都保留原始文本哪怕是混合在一起的段落文本也没关系这样信息不丢后面用正则或者 split 去拆即可。再补一个字段提取的小技巧如果要单独把年份和地区拆成两列用这一行就能搞定。import re def parse_info(info_text: str) - dict: parts [p.strip() for p in info_text.split(/)] # 示例: [1994, 美国, 犯罪 剧情] year re.search(r\d{4}, parts[0]) return { year: year.group() if year else , region: parts[1] if len(parts) 1 else , genre: parts[2] if len(parts) 2 else , }靠正则从字符串里抽出四位年份再用长度判断兜底。为什么用re而不是直接切片因为有些条目的年份可能是1994(中国大陆)或1994-01-01这种形态直接切片会漏。与常见误用不同不要在爬虫阶段就把这些逻辑塞进页面解析里遇到页面结构调整你就得全量重写分离采集与解析出错了只改一个函数就好。3. 数据清洗与存储把 csv 变成能直接做分析的 DataFrame3.1 pandas 数据处理的基本盘读取、去重、类型转换数据拿到手之后接下来最重要的不是画图而是把数据整理成“可分析状态”。用 pandas 读取刚才的 csv然后做四件事去掉完全重复的行、把评分列从字符串转成浮点数、从信息列里拆出年份和类型、过滤掉缺失严重的行。这是任何电影分析系统落地时都会遇到的基础数据预处理步骤。import pandas as pd df pd.read_csv(douban_top250.csv, encodingutf-8-sig) # 1) 去重以标题为准保留第一条出现的记录 df df.drop_duplicates(subset[title]) # 2) 评分转数值出错时置空coerce 表示无法转换则取 NaN df[rating] pd.to_numeric(df[rating], errorscoerce) # 3) 拆分info字段生成三个新列 infos df[info].apply(parse_info) df[year] infos.apply(lambda x: x[year]) df[region] infos.apply(lambda x: x[region]) df[genre] infos.apply(lambda x: x[genre]) # 4) 删除关键字段为空的行 df df.dropna(subset[rating, year]) df df.reset_index(dropTrue) print(df.shape) print(df.dtypes)这段代码里有两个关键参数需要刻意理解。subset[title]是去重的判断依据你可以改成[title, year]因为两部同名电影可能年份不同errorscoerce是pd.to_numeric的参数它让非法输入变为NaN而不是抛异常这样你可以用dropna统一过滤掉脏数据。实际项目中我一般会把print(df.shape)放在清洗前后各一次人工对比减少量以此确认清洗逻辑没有误伤数据。清洗完之后建议顺手统计一下年份范围、评分的分布、类型字段的取值情况这些就是你做可视化时“要讲什么故事”的素材。比如你会发现评分集中在小数点后一位8.5~9.0 区间的数量最多这就可以成为分析指标的起点。3.2 存储选型为什么毕设不一定要上 MySQL数据清洗到这份上接下来面临一个问题数据存哪很多毕设会把“用了 MySQL”当卖点但对于 250 条或 5000 条的电影数据上数据库的实际价值不大还增加部署复杂度。我一般会分两种情况处理如果系统里需要交互式查询按年份筛选、按类型筛选、支持组合条件就用 SQLite零配置文件、Python 内置支持如果只是纯静态展示直接把清洗后的 DataFrame 导出成新的 csv 或 Excel 即可。SQLite 是毕设里性价比最高的折中方案。它只有一个 .db 文件不需要安装服务端还能写 SQL 展示你的数据库能力。import sqlite3 conn sqlite3.connect(movies.db) df.to_sql(movie_top250, conn, if_existsreplace, indexFalse) conn.close()to_sql这个接口是 pandas 封装的等价于建表 逐行插入。if_existsreplace表示每次运行都覆盖旧表适合开发调参阶段如果到了演示阶段建议改成append或者直接删除分支避免误操作把数据清掉。这里有个细节df.to_sql默认会按 pandas 的列类型推断 SQLite 的字段类型如果后续要写 SQL 做范围查询建议在to_sql之前手动把年份列转成 int。否则 SQLite 里存的是文本WHERE year BETWEEN 2000 AND 2010查不准。3.3 构造“能被答辩追问”的分析指标数据入库不是终点分析才是。毕设答辩时评委最常问的一句话是“你这指标体系是怎么设计的”如果回答“就是把年份和评分画一下”会显得没有分析逻辑。我建议你在系统里定义几组固定的分析维度每一组对应一张图。常见的分析切入点有五个电影年份分布看产量趋势、评分分布直方图看整体评分结构、类型与评分的关系箱线图或均值对比、地区与数量的关系柱状图、评分与评价人数的相关性散点图。这几个维度全部能从刚才的 DataFrame 计算出来不需要额外采集数据。# 1) 各年份电影数量 year_count df.groupby(year).size().reset_index(namecount) # 2) 评分分布按0.5分一档 rating_bins pd.cut(df[rating], bins[8.0, 8.5, 9.0, 9.5, 10.0], rightFalse) rating_dist rating_bins.value_counts().sort_index() # 3) 类型与评分先拆类型字段展开成多行再算均值 genre_rating df.assign(genredf[genre].str.split( )) genre_rating genre_rating.explode(genre) genre_rating_mean genre_rating.groupby(genre)[rating].mean().sort_values(ascendingFalse)上面这段代码有三个点值得解释。pd.cut的bins我特意留了 8.0 以下没放进区间是因为 Top250 最低分约 8.2分箱能呈现高分段内部的密度差异rightFalse让区间左闭右开评分 8.5 会落在[8.5, 9.0)而不是[8.0, 8.5)这是处理浮点边界时常见的坑。explode(genre)是把“犯罪 剧情”这种多类型文本拆成两行这样每部电影参与多次统计算均值时单个类型样本量更充足。如果你在答辩时需要回答“为什么用均值不用中位数”补一句即可评分数据近似正态且无极端值均值与中位数差异极小用均值更直观。这样整个分析逻辑就闭环了。4. 数据可视化落地从 Matplotlib 静态图到 ECharts 交互看板4.1 PyECharts 4 / 5 系列选型与渲染方式选择到了可视化这一步你就需要决定“用什么画图”。通常有两类选择Matplotlib Seaborn 适合生成论文插图出的是静态 PNG规格统一PyECharts 适合做网页动态展示图表可以带 tooltip、缩放、图例切换等交互。对毕业设计而言我的建议是两者结合使用论文/报告里插图用 Matplotlib系统演示界面用 PyECharts两个都出现会让你的工作量看起来更饱满也是 ECharts 数据可视化大屏这类项目常见的落地方式。PyECharts 的使用门槛比 Matplotlib 稍高一点它采用“链式调用”风格。核心逻辑是先创建一个图对象设置add()方法传入数据再用render()输出 html 文件。下面是一个基础的柱状图示例展示各类型电影的平均评分。from pyecharts.charts import Bar from pyecharts import options as opts # genre_rating_mean 来自上一节的聚合结果 categories genre_rating_mean.index.tolist() values [round(v, 2) for v in genre_rating_mean.values] bar ( Bar() .add_xaxis(categories) .add_yaxis(平均评分, values) .set_global_opts( title_optsopts.TitleOpts(title不同类型电影平均评分对比), yaxis_optsopts.AxisOpts(min_8.0, max_9.5), # 控制Y轴范围突出差异 ) ) bar.render(genre_rating_bar.html)add_yaxis是核心的数据注入接口set_global_opts用来配置标题、坐标轴、图例等全局属性。注意到我把min_设为 8.0、max_设为 9.5这是关键参数不设的话ECharts 会从 0 开始显示所有柱子的高度差会被压缩得几乎看不见观感很差。这类坐标轴范围控制适用于所有评分对比场景也是企业级数据可视化交付时最先被画图人员要求调整的细节。PyECharts 的输出文件是一个完整的 html你可以把它嵌入到 Flask 模板中。静态图与动态图的技术栈差异就在这一步体现出来Matplotlib 画完是“图片文件”PyECharts 画完是“交互组件”后者能直接放到 Web 前程里复用。4.2 把图表挂到 Flask 页面里搭建最小可演示的 Web 系统完整的数据可视化系统不能只停留在“生成一堆 html 文件”的阶段。你需要一个入口页面把多张图表组织在一起供用户切换查看。Flask 是 Python 生态里做这件事最轻的方案也符合“Python 可视化系统”这个标题的预期。下面是一个最小可运行的 Flask PyECharts 集成模板。先写 Python 主程序from flask import Flask, render_template from pyecharts.charts import Bar, Line, Pie from pyecharts import options as opts import pandas as pd app Flask(__name__) # 启动时加载一次数据避免每次请求都读csv df pd.read_csv(douban_top250_clean.csv, encodingutf-8-sig) def create_year_chart(): year_count df.groupby(year).size().sort_index() line ( Line() .add_xaxis(year_count.index.tolist()) .add_yaxis(电影数量, year_count.values.tolist()) .set_global_opts(title_optsopts.TitleOpts(title电影数量年度趋势)) ) return line app.route(/) def index(): chart create_year_chart() return render_template(index.html, chart_htmlchart.render_embed()) if __name__ __main__: app.run(debugTrue)对应的templates/index.html文件核心部分!DOCTYPE html html head meta charsetutf-8 title电影数据分析系统/title /head body stylefont-family: Microsoft YaHei, sans-serif; margin: 30px; h2基于Python的电影数据可视化分析系统/h2 div{{ chart_html | safe }}/div /body /html注意render_template(index.html, chart_htmlchart.render_embed())这行它是整条链路的关键render_embed()会返回一段完整的 JavaScript HTML 字符串模板里用| safe告诉 Jinja2 这段字符是预先渲染好的不需要转义。如果不加safe过滤器页面上会直接显示div源码。.py 文件里app.run(debugTrue)开启调试模式改动后自动重启但毕设演示要记得关掉或设置为False否则他人访问你的端口时能看到调试器交互界面存在安全隐患。4.3 图表配色与布局让演示效果接近“数据可视化大屏”技术实现走通以后真正拉开分数差距的是“展示细节”。同一个数据直接使用默认主题和花 20 分钟调过的配色在答辩现场的观感完全不同。我不建议用 Matplotlib 默认的蓝色列表样式也不建议在 PyECharts 里反复堆砌颜色参数。我一般是直接复用现成的配色方案例如 PyECharts 内置的chalk、infographic主题或者指定自定义颜色数组。自定义颜色的示例很简单在add_yaxis时通过itemstyle_opts控制from pyecharts.charts import Pie from pyecharts import options as opts genre_count df[region].value_counts() pie ( Pie() .add( series_name地区分布, data_pair[list(z) for z in zip(genre_count.index, genre_count.values)], radius[35%, 60%], # 内径35%外径60%形成环形图 ) .set_global_opts(title_optsopts.TitleOpts(title产地分布占比)) )radius[35%, 60%]是做出环形图的关键参数它的含义是内径和外径相对容器宽度的比例。这个比例做出来视觉效果比实心饼图更清爽也更贴近毕设系统中常见的可视化风格。页面层面用 CSS 的display: flex把多个图表容器横向排列配合之前 Flask 传图表的方式就能在首页呈现两个或三个图表并排的效果再往上做就是一张简单的大屏幕看板。5. 参数调优与避坑我用这套系统跑数据踩过的 5 个坑5.1 评分列读进来是 object 类型画图时全部报错现象从 csv 读取数据后df[rating].dtype显示为objectplt.plot时提示无法将字符串转换为浮点数。原因csv 中评分列存在空值或空格字符。pandas 读取时整列被推断为字符串类型后续没有做类型转换直接使用。解决读取后统一执行df[rating] pd.to_numeric(df[rating], errorscoerce)然后dropna去除空行。这个动作必须放在任何聚合统计之前。我在排查时发现一个隐蔽变种某些爬虫将评分写成了9.7 带尾随空格errorscoerce可以完美处理这种情况比strip()后再转换更省事。5.2 PyECharts 生成的 html 在中文路径下无法打开现象文件放到D:\毕业论文\charts\演示.html路径下双击浏览器显示一片空白控制台报 ECharts 资源加载失败。原因PyECharts 默认通过相对路径引用本地 js 资源在含中文或特殊字符的路径下某些浏览器的静态资源加载策略会出问题。解决换用chart.render(demo.html)放到纯英文路径下或者调用bar.render_embed()将图表直接嵌入 Flask 模板跳过独立的 html 文件。项目目录结构我建议从一开始就建成英文命名整个毕设源码压缩包和演示文件都遵守这个约定避免莫名奇妙的编码问题。5.3 爬虫跑一半被拒绝访问提示 418 或触发验证码现象循环请求到第3页或第4页时resp.status_code变成 418页面内容不再是电影列表。原因请求频率过高同一 IP 在短时间内被网站限流。也可能是部分页面返回了反爬挑战页。解决加重试机制和延时。延时设置在 2~5 秒之间不要固定间隔用time.sleep(random.uniform(2, 5))模拟人工浏览节奏。再增加一个简单的重试逻辑请求失败后等待更长时间最多重试 3 次。这条建议适用于几乎所有站点的数据采集“睡一觉再继续”比“疯狂重试”有效得多。5.4 Flask 模板里图表不显示只有一串警告文字现象页面能打开但图表区域显示类似 “This is an ECharts option...” 的字符串或 JavaScript 代码。原因render_embed()返回值在 Jinja2 模板中被自动转义浏览器把标签当作普通文本显示了。这是新手最容易遇到的情况。解决模板中必须使用| safe过滤器即写成div{{ chart_html | safe }}/div。如果使用的是render_template_string而非模板文件同样需要加| safe。这个坑每天都会“坑”掉若干人但解决方案只有这一行记住即好。5.5 打包部署时 pyecharts 资源文件缺失页面图表空白现象在自己电脑上运行正常把项目复制到另一台电脑或打包成 exe 后网页图表加载不出来。原因PyECharts 的静态资源js 文件存在于库目录内复制项目时未被同步过去或打包工具未将 ECharts 的 js 资源包含进产物。解决确认复制完整的项目目录包括templates、static和venv依赖打包成 exe 时在打包配置里添加pyecharts的资源目录。最稳妥的办法是不打包直接让对方安装 Python 依赖后运行源码毕设答辩场景下用源码演示反而更能体现工作量。6. 进阶把数据可视化分析系统做成“有结论”的演示项目毕设答辩的时间有限通常在 10 到 15 分钟之间。你能演示的操作其实是有限的。与其让评委在系统里到处点不如把核心分析结论直接呈现在首页让评委一眼看到“这个系统得出了什么发现”。我建议你做一个“分析结论区”用卡片或文本块列出 3 到 4 条关键发现例如“评分 9.0 以上的电影占比约 12%”“2000 年后电影数量占比超过一半”“剧情类电影的平均评分显著高于动作类”。每条结论后面配上对应图表入口讲的时候有的放矢。如果你想再进一步有两个高性价比的进阶方向。第一个是给 Flask 系统增加一个搜索框支持按电影名查询评分和类型用df[df[title].str.contains(keyword)]即可实现第二个是做一个简单的年度票房或评分排行榜用df.nlargest(10, rating)快速计算并展示 Top10 榜单。这两个功能的代码量都很小但会显著提升系统完整性让项目看起来更接近一个真正可用的分析系统。最后一个血泪教训从开发的第一天起就用虚拟环境管理依赖不要直接使用全局 Python 环境安装包。我见过不止一个同学把 pandas、pyecharts 装进系统 Python然后因为版本冲突导致 PyCharm 里的解释器找不到模块白白浪费一整天。用python -m venv venv创建环境用pip install -r requirements.txt安装依赖把这份文件放进项目根目录。这样无论换电脑还是发给导师几分钟就能恢复全环境希望这步你能少走弯路希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。