Django电影数据分析与可视化系统完整实战解析
发布时间:2026/10/1 16:41:16来源:尧图网络
简介这份代码分享资源完整提供了一套基于Python与Django的电影数据分析和可视化系统面向具备一定Python基础、希望掌握Web开发与数据可视化完整流程的开发者、学生或数据分析爱好者。系统功能覆盖用户登录鉴权、网络爬虫采集电影数据、依据用户偏好推荐TOP10影片、按电影名/导演/演员模糊搜索并展示影片详情以及数据可视化大盘可视化部分包括电影年代、产地、类型的饼状图与柱状图和基于评价文本的词云图分析。压缩包内共42个文件包含8个Python源文件、11个HTML模板页面、8个CSV数据集、SQLite数据库文件、CSS样式、环境安装说明与启动文档整体大小约299.78MB目录结构清晰便于按功能模块逐项对照学习。目前已有1040人学习下载内容可直接运行调试。通过学习源码可掌握Django项目搭建、爬虫解析入库、数据清洗、图表渲染与词云生成等实用技能也能用于课程设计、毕业设计或个人项目二次开发。1. 电影数据分析与可视化这份代码包到底能拿来做什么做电影数据分析最常遇到的尴尬不是不会写爬虫也不是不会调图表而是数据还没攒够、项目就已经烂尾了。这套基于 Python Django 的电影数据分析与可视化系统最值钱的地方在于它把数据这条线直接铺好了——项目里内置了 db.sqlite3 预置数据库以及 movie_data 下的多份 CSV 原始数据拿到代码包按顺序跑一遍就能在浏览器里看到一个完整的可交互分析界面包含用户登录、电影搜索、Top10 推荐、年代产地类型分布图表和评论词云。比起那些只给「半成品代码 空数据库」的分享这份资源对刚接触 Django 数据分析实战的人友好得多。适合两类人一是想完整走一遍 Web 分析系统开发流程的学生二是需要快速搭一个数据分析演示原型、不想从零造轮子的从业者。2. 先把 Django 项目跑起来目录结构与环境安装里的门道2.1 从文件清单反推整个系统的数据流与模块划分拿到解压后的代码包首先别急着双击 README先看一遍目录结构心里有个全局图。这份项目正文暴露的信息很完整顶层是一个标准 Django 工程内层有一个业务应用app 目录里包含 views.py、urls.py、models 相关文件这是 Django 的 MTV 架构——templates 管页面呈现views.py 管业务逻辑models 与 SQLite 数据库打交道。真正值得停下来看的是数据相关的几组文件。db.sqlite3 是已经初始化好的 SQLite 数据库里面不仅有表结构还有预置数据这意味着你不需要执行 makemigrations 和 migrate 就能直接把服务跑起来。如果你改过 models 再动数据库那另说后面我会专门讲这个坑。movie_data 目录下则放着 movies.csv、users.csv、person.csv、ratings.csv、comments.csv 五份原始数据另外还有一份 stopwords.txt 停用词表——这个词表专门供词云分析模块用是过滤「的、了、电影、一部」这类无意义高频词的过滤字典。templates 目录值得逐个扫一眼base.html 是整站母版user_base.html 是登录后的页面框架fc_charts.html 对应数据分析可视化界面fc_cloud.html 是词云展示页fc_search.html 是搜索页fc_movies.html 是电影列表页。这种命名方式很直白fc 开头的基本都是 functional 页面。static 目录里是 css 和图片资源manage.py 是 Django 的启动入口。整个系统的数据流向是爬虫或手动准备的 CSV 文件 → 通过脚本导入 SQLite → Django 视图从数据库聚合数据 → 模板页面用图标库渲染成图表。2.2 环境准备与三步启动虚拟环境隔离与依赖安装顺序这套代码包在 Windows 和 macOS 上都能跑关键是 Python 版本别太老建议 3.8 以上。Django 项目最怕的就是全局环境打架所以第一步永远是建虚拟环境。用 venv 还是 conda 都可以我一般习惯用 venv轻量且 Python 3.8 以上自带不需要额外装东西。# 1. 创建并激活虚拟环境 python -m venv movie_env # Windows 激活方式 movie_env\Scripts\activate # macOS / Linux 激活方式 source movie_env/bin/activate # 2. 安装核心依赖按实际代码包的依赖清单装缺哪个补哪个 pip install django pip install pandas pip install jieba pip install requests pip install beautifulsoup4 # 3. 进入项目根目录并启动服务 cd 项目根目录 python manage.py runserver首次启动时访问 http://127.0.0.1:8000 就能看到登录页。如果这一步直接报 ModuleNotFoundError说明某个依赖没装全如果报数据库相关错误基本可以确定是你的 Python 版本和项目里 db.sqlite3 的兼容性出了岔子。这里有个容易忽略的动作项目正文里那份「环境安装 环境启动操作.txt」里面通常会写针对这套代码的具体版本依赖有些作者甚至会在里面标注 Python 配套版本先读它再动手能少走弯路。参数层面runserver 默认监听 8000 端口如果想换端口或者让局域网内其他设备访问可以加参数python manage.py runserver 0.0.0.0:8080但 0.0.0.0 是开放所有网卡接口实际部署在公司网络里要小心仅限本地调试时图方便用。还有人喜欢加--noreload关掉自动重载这在调试多进程时会省心日常开发没必要。特别提醒一个玄学问题网上不少分享代码包里没有 requirements.txt也没有明确的依赖版本表这时候不要盲目 pip install 一堆包。正确做法是先跑一遍看报错缺什么装什么。Django 版本尤其敏感4.x 和 3.x 在 url 配置写法上有细微差别如果代码里用的是 urls.py 中 re_path 这类写法Django 2.0 以下版本根本识别不了。3. 数据怎么流进系统CSV 文件、导入脚本与 SQLite 的三角关系3.1 五份 CSV 文件的字段设计与数据用途映射电影分析系统的核心资产是数据而这份代码包最厚道的地方就是 movie_data 目录下那五份 CSV。在动手改代码之前先把这些数据文件的字段和用途对应起来后面所有图表、搜索、推荐逻辑都围绕这些字段展开。文件关键字段对应的功能模块movies.csv电影名、导演、主演、年代、产地、类型、评分电影列表、Top10 推荐、年代/产地/类型统计图users.csv用户 ID、用户名、密码、偏好标签用户登录、个性化 Top10person.csv演职员 ID、姓名、参演作品搜索模块按演员搜ratings.csv用户 ID、电影 ID、评分、时间评分分布分析、推荐算法comments.csv电影 ID、评论内容、情感倾向词云图分析从这张表能看出系统的设计思路movies.csv 是维度表提供电影的静态属性ratings.csv 是行为表记录用户打分comments.csv 是文本数据专门喂给词云模块。用户登录后看到的「根据用户偏好展示 TOP10 电影」本质上是拿 users.csv 里的偏好标签去匹配 movies.csv 里电影的「类型」字段再按评分排序取前 10。这里有一个容易被误解的点这些 CSV 文件并不是每次启动系统时都会重新加载的实时数据源。它们更像是「数据仓库」只有在执行 create_data.py 脚本时才会被读入 SQLite。系统运行期间Django 的视图函数只从 SQLite 里取数。这个设计的好处是分析速度不依赖 CSV 的读取效率坏处是你每次更新 CSV 数据后都必须重新跑一次导入脚本否则查询到的还是旧数据。很多新手在这里翻车改了 movies.csv 里的数据刷新页面没有任何变化就以为是缓存问题其实是因为数据根本没进库。3.2 create_data.py 的导入逻辑读 CSV、清洗、去重、写 SQLitecreate_data.py 是这套系统的数据闸门它的职责是把你手上那些多少有点脏的 CSV 数据清洗干净并写入 SQLite。Django 项目里这种脚本通常放在 manage.py 同级的 scripts 目录或者干脆放在项目根目录下通过python create_data.py直接执行。核心逻辑长这样import sqlite3 import pandas as pd # 连接 SQLite 数据库 conn sqlite3.connect(db.sqlite3) cursor conn.cursor() # 读取电影基础数据 movies_df pd.read_csv(movie_data/movies.csv) # 清洗去除评分为空或电影名为空的记录 movies_df movies_df.dropna(subset[title, rating]) # 去重同一部电影名只保留第一条记录 movies_df movies_df.drop_duplicates(subset[title], keepfirst) # 逐行写入数据库用 INSERT OR REPLACE 避免主键冲突 for _, row in movies_df.iterrows(): cursor.execute( INSERT OR REPLACE INTO movies (mid, title, director, actor, year, region, type, rating) VALUES (?, ?, ?, ?, ?, ?, ?, ?), (row[mid], row[title], row[director], row[actor], row[year], row[region], row[type], row[rating]) ) conn.commit() conn.close() print(f成功导入 {len(movies_df)} 条电影数据)这段代码用 pandas 做数据清洗逐行写入 SQLite。参数说明dropna 里指定 subset 是为了只删除关键字段缺失的行避免误删掉「导演没填但评分有效」的记录drop_duplicates 的 keepfirst 表示当同一部电影出现多次时以 CSV 中首次出现的记录为准。INSERT OR REPLACE 则保证了脚本可以重复执行不会因为重复导入而报主键冲突。实际执行时你会遇到不少情况——CSV 里年份字段有的是「2024」有的是「2024年」有的是纯数字产地字段里「美国」和「USA」混用类型字段里「喜剧,剧情」用逗号分隔。如果不对这些做标准化后续统计图表的聚合结果就全乱套了。我一般会在导入前增加一步预处理对年份字段做 astype(str).str.extract((\d{4})) 提取四位数字对类型字段统一做 split 后展开保证每个类型标签独占一条统计记录。另外注意 ratings.csv 和 comments.csv 的导入顺序必须先导入 movies.csv 生成电影 ID再导入 ratings 和 comments因为后两张表的外键要引用电影 ID 和用户 ID。如果顺序反了外键关联会变成一堆悬空的空引用搜索和推荐功能返回的数据就会莫名其妙少一截。4. 可视化核心模块从 ECharts 图表到词云图的完整实现链路4.1 视图函数怎么组织数据按关键词聚合后传给模板看完了数据导入再来拆解可视化这块的实际开发逻辑。系统里的「数据分析可视化界面」是让用户选择关键词比如年代、产地、类型来查看对应的饼状图或柱状图。这个功能在 Django 里实现时最直观的方式是写一个统一的视图函数接收前端传上来的「分类维度」参数按维度分组聚合后把聚合结果塞给模板去渲染。# views.py 中的可视化聚合接口示例代码按项目实际字段名调整 import json from django.shortcuts import render from django.db.models import Count from .models import Movie def fc_charts_view(request): # 从 GET 请求中取用户选择的分析维度year / region / type dimension request.GET.get(dimension, type) # 从 GET 请求中取图表类型pie / bar chart_type request.GET.get(chart_type, pie) # 按维度分组统计每组的电影数量 if dimension year: records Movie.objects.values(year).annotate(countCount(mid)).order_by(-count) elif dimension region: records Movie.objects.values(region).annotate(countCount(mid)).order_by(-count) else: records Movie.objects.values(type).annotate(countCount(mid)).order_by(-count) # 组织前端 ECharts 需要的结构名称列表 数值列表 categories [str(item[dimension]) for item in records] values [item[count] for item in records] context { categories: json.dumps(categories, ensure_asciiFalse), values: json.dumps(values), chart_type: chart_type, dimension: dimension, } return render(request, fc_charts.html, context)这段代码的关键在于用 Django ORM 的 values() 配合 annotate(Count) 做分组统计避免了手动写 SQL 的繁琐也天然规避了 SQL 注入风险。charts 页面接收 dimension 和 chart_type 两个参数前者决定按什么字段聚合后者决定前端渲染成饼图还是柱状图。默认值是 type电影类型因为类型分布通常是最直观的分析入口。这里有一个容易忽略的细节json.dumps 里必须加 ensure_asciiFalse否则中文分类名会被转成 \uXXXX 的 Unicode 转义序列传到前端 JS 里显示成一堆乱码。这是所有 Django ECharts 项目最常见的翻车点之一后面排查章节会单独展开。此外词云分析走的是另一条链路。comments.csv 里的评论文本通过 jieba 分词后统计词频再把词频数据传给词云渲染组件。视图函数里通常先用 jieba.lcut() 把所有评论切成词列表过滤掉 stopwords.txt 里的停用词然后交给 collections.Counter 统计出词频最高的前 100 个词按「词: 频次」的结构传给模板。这个模块对数据量比较敏感如果 comments.csv 只有几百条评论词云效果会很稀疏如果文本质量差、停用词表不完整词云里会被「电影」「一部」「这部」这种词刷屏。4.2 模板渲染与图表参数ECharts 的 option 配置细节后端把数据聚合好了前端图表渲染的核心在 fc_charts.html。这套系统用 ECharts 做图表渲染饼图和柱状图共用一套数据、切换图表类型关键在 option 配置的动态切换。!-- fc_charts.html 中的 ECharts 初始化片段 -- script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script div idmain_chart stylewidth: 100%; height: 520px;/div script // 后端通过模板上下文注入的数据 var categories {{ categories|safe }}; var values {{ values|safe }}; var chartType {{ chart_type }}; var chart echarts.init(document.getElementById(main_chart)); var option { title: { text: 电影数据分布 }, tooltip: { trigger: item }, legend: { bottom: 0% }, series: [{ name: 电影数量, type: chartType, // 动态切换 pie 或 bar data: categories.map(function(name, index) { return { name: name, value: values[index] }; }), radius: chartType pie ? 60% : undefined, itemStyle: { // 针对柱状图给不同柱子分配不同颜色 color: function(params) { var colors [#5470c6, #91cc75, #fac858, #ee6666, #73c0de, #3ba272]; return colors[params.dataIndex % colors.length]; } } }] }; chart.setOption(option); /script这段模板代码里categories 和 values 是从后端 json.dumps 传入的模板变量用|safe过滤器防止 Django 自动转义破坏 JSON 结构。chartType 变量决定 ECharts 用饼图还是柱状图渲染同一份数据这种设计让用户在前端切换图表类型时不必重新请求后端——数据是一样的换一种视觉表达方式而已。pie 图的 radius 设置成 60% 是环状效果想做成实心饼图可以改成[0%, 60%]的形式外层半径 60% 内层 0% 就是实心。词云页的渲染思路类似但组件不同。词云通常用 ECharts 的 wordCloud 插件或者独立的前端词云库。数据格式是词频变量 word_freq结构类似[{name: 科幻, value: 56}, {name: 悬疑, value: 43}]词频 value 越高渲染出的字号越大。这里要注意的是词云图表对数据量有下限要求少于 30 个有效词时图形会很单薄建议后端统计时至少保留前 100 个高频词并设定最低频次阈值过滤掉只出现过一两次的噪声词。4.3 爬虫数据收集模块连接外部数据源的正确姿势正文里提到「爬虫电影数据收集」功能模块这是整套系统数据自更新的入口。代码包里通常会用 requests BeautifulSoup 写一个数据采集脚本抓取电影网站的信息解析后生成或追加到 movies.csv再通过 create_data.py 入库。这个模块和前端展示是解耦的——爬虫只管产出 CSV展示只管读 SQLite。如果只是为了跑通演示这一段不碰也罢但如果你想拿它持续更新数据就要注意爬虫的目标站结构可能会变导致选择器失效。requests 请求头里最好带上 User-Agent否则有些站会直接拒绝连接解析时优先用 CSS 选择器而不是正则表达式可维护性会好很多。至于抓下来的数据质量最稳的做法是先在本地打印出前几条结果检查字段是否完整再决定是否写入 CSV不要上来就全量写入否则脏数据进了库里后面图表直接变形。5. 从登录到搜索再到图表展示五个高频翻车点排查与避坑记录5.1 中文乱码CSV 读取和 JSON 传参双重编码陷阱现象页面上的图表分类名显示成\u559c\u5267或者æ´å§这类乱码数据库里的中文都是正常的。原因两个层面。第一层是 CSV 文件读取时编码不对pandas 默认用 utf-8 读但很多爬虫导出的 CSV 是 gbk 或 gb18030 编码读出来就是乱码第二层是 Django 视图往模板传 JSON 时没指定 ensure_asciiFalse导致中文被转成 Unicode 转义序列。解决读取 CSV 时显式指定编码pd.read_csv(movies.csv, encodingutf-8)如果报错就改成encodinggbk或errorsignore视图函数里 json.dumps 时一律加ensure_asciiFalse。这两处都改到位中文乱码问题能消灭九成。还有一成是在 HTML 模板头部没写meta charsetutf-8浏览器按旧编码解析导致渲染出来是乱码检查一下 base.html 里有没有这行。5.2 停用词不生效词云被「电影、一部、这部」刷屏现象词云图生成后视觉上全是「电影」「一部」「这部」这类词真正的分析价值很低。原因stopwords.txt 虽然存在但加载逻辑没有真正连到分词流程里。有些代码里只读取了文件却没有按行 split或者停用词匹配时把停用词文件里带空格的行原样拿去比较导致永远匹配不上。另一个常见原因是新增停用词后没有重新启动 Django 服务Python 模块的全局变量被缓存文件改了但不生效。解决检查分词模块里加载停用词的代码是否完整常见写法是用 set 存储停用词——stopwords set(line.strip() for line in open(stopwords.txt, encodingutf-8))——在分词后过滤时对每个词做if word not in stopwords的判断。另外每次修改 stopwords.txt 后必须重启 runserver或者干脆在加载代码里加一个时间戳缓存机制否则改动不会生效。这是词云模块最典型的「改了等于没改」翻车点。5.3 模糊搜索失效搜索接口返回空列表现象在搜索框输入电影名、导演名或演员名前端页面永远显示「未找到相关电影」。原因三种可能。第一是搜索接口用的字段名和数据库实际字段名对不上比如视图里查的是 actor数据库里字段叫 actors查出来当然是空第二是模糊匹配用的是 SQL 的精确匹配输入「战狼」搜不到「战狼2」第三是模板里的搜索表单 name 属性和视图里 request.GET.get() 的参数名不一致参数取不到值后端直接按空字符串查。解决查看 view 函数里搜索相关的代码把查询方式改为filter(title__icontainskeyword)这种 Django ORM 的模糊查询icontains 不区分大小写且包含即匹配。表单提交的 name 属性和视图里的参数名必须完全一致这个只能逐个对没有捷径。另外建议后端对 keyword 做 strip 处理去掉首尾空格否则用户多打一个空格就查不到结果体验很僵硬。5.4 图表区域空白后端数据传到了但前端渲染不出来现象页面上饼图柱状图区域一片空白右键查看页面源码categories 和 values 变量有值但 ECharts 不渲染。原因最常见的是 JS 报错打开浏览器控制台就会发现 echarts.min.js 加载失败或者 init 的 DOM 元素还没渲染完成就开始初始化。另一个原因是 json.dumps 后的数据里含有 undefined 或 nullECharts 的 series.data 数组遇到空值会直接罢工整个图表都不显示。解决把 ECharts 的 CDN 地址从外网换成国内镜像或本地 static 目录里的资源防止网络问题导致加载失败在初始化前加window.onload或DOMContentLoaded保证 DOM 就绪再 init后端聚合数据时把 None 值统一替换成 0 或空字符串保证传给前端的数组元素是合法类型。还有一个隐蔽问题如果系列数据里单个分类名特别长比如「冒险 动作 科幻」ECharts 的 axisLabel 默认会截断显示但不会导致空白只是看起来像少了数据。5.5 Django 版本升级后 models 迁移出错现象项目本来跑得好好的某天想升级 Django 大版本升级后 migrate 报一堆冲突错误或者启动直接报Model class ... doesnt declare an explicit app_label。原因Django 大版本之间特别是 2.x 到 4.x在 model 元数据、URL 配置、中间件写法上都有 breaking change旧项目的 models 和 urls 没有同步迁移适配。解决不要轻易动 Django 版本这套代码包是基于某个特定版本写的就用那个版本跑。如果非升不可先将旧数据库导出为 JSON fixture再在新版本里通过loaddata恢复同时逐行检查 urls.py 里的 path 和 re_path 写法。更务实的建议是锁住虚拟环境里的 Django 版本在 requirements.txt 里写上Django3.2.x这种精确版本避免 pip 升级时把环境搞乱。这是很多初学 Django 的人最容易忽略的「环境锁」概念但它能避免大量无意义的排错时间。6. 再进阶一步把词云做成真正能说明问题的分析工具可视化系统的价值在图表本身更在图表背后的分析逻辑。词云这个东西做出来漂亮很容易做得「有信息量」才是真功夫。我自己的经验是拿到任何评论数据集第一步绝对不是直接画图而是先跑一遍词频统计打印出 TOP20 高频词看看停用词表有没有漏网之鱼。from collections import Counter import jieba # 加载停用词 with open(stopwords.txt, encodingutf-8) as f: stopwords set(line.strip() for line in f) # 读取评论 with open(movie_data/comments.csv, encodingutf-8) as f: texts [line.split(,)[-1].strip() for line in f if line.strip()] # 分词并过滤 words [] for text in texts: for word in jieba.lcut(text): if len(word) 1 and word not in stopwords: words.append(word) counter Counter(words) # 打印 TOP20 验证词频分布 for word, count in counter.most_common(20): print(word, count)这段代码每次执行时先打印 TOP20我会根据输出决定要不要往停用词表里追加干扰项。比如打印结果里出现「电影」「真的」「一部」且频率极高就直接把它们加进 stopwords.txt再跑一遍。第二层进阶是词频统计与电影评分的联动——把评分高于 8 的电影评论单独聚合成一组低频词单独一组各自生成词云后再对比这样词云就从一个「好看的大字图」变成了「能看出高分片和低分片讨论差异」的分析工具。条件允许的话还可以给词云加形状约束用一张电影胶片或者爆米花桶的 PNG 图片作为遮罩词云的词频分布不变但视觉冲击力会明显提升。实现方式是用词云库中 mask 参数接收一张黑白图片路径词只会填充在图片黑色区域。这个技巧演示效果很加分尤其适合期末答辩或项目汇报场景。我曾经做过一个电影评论分析项目第一版词云图里「电影」两个字大到把「悬疑」和「剧情」都挤到边角去了当时还不明白怎么回事后来一查停用词表发现压根没收录它。从那以后我每次做文本类分析都强制先跑一遍 TOP20 词频统计再决定停用词和可视化方案不跳过这一步这个习惯帮我省下的排错时间远超过多写几行代码的时间。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网