资讯详情 基于Python Django的舆情分析系统:数据采集、情感分析与可视化实践
📅 2026/10/9 4:29:41
简介「Python网络舆情分析系统」是一套基于Django框架的Web应用完整项目源码包面向具备基础编程能力、希望深入理解Python后端开发的高校学生与开发者尤其适合作为毕业设计或课程实践参考。资源共290个文件压缩包约93.5MB内容涵盖Python源码.py、编译文件.pyc、前端样式与脚本.css/.js、HTML页面、SQL数据库脚本以及说明文档、PPT等可支撑从环境搭建到功能调试的完整学习链路。目前已吸引461人学习下载。通过研究源码读者可以掌握Django项目的分层设计、数据库集成方法及可扩展管理系统的构建思路配套的文档与数据库脚本则便于快速还原运行环境PPT和LW材料可用于答辩汇报或技术分享。整个包目录结构清晰适合边读源码边对照实践系统性提升Python Web开发能力。1. 基于 Python 的舆情分析系统不只是一份能跑的 Django 毕设源码手里这套 Python 网络舆情分析系统拿到手第一眼可能会被“项目源码 数据库脚本 文档 LW PPT”这几个字误导以为又是一份纯应付答辩的模板工程。但把源码拆开看下来它实际上是一个标准的 Django 管理后台 舆情数据采集分析的前后端一体项目数据库脚本、ORM 模型、后台管理页面、图表展示都有完整实现拿来复现 Python 实战项目、补数据库集成经验甚至是直接改造成毕设都够用。对新手来说这份源码最大的好处是能同时看到“Web 页面怎么和数据库交互”“舆情数据怎么从入库到展示”这两条完整链路对熟手来说它是一份能快速扩展的骨架——加采集器、换分词库、接情感分析接口改造点都很清楚。下面按我拆项目的习惯从技术栈选型、数据库脚本导入、核心模块实现、参数调节到避坑记录一条线讲完。2. 技术栈与数据库设计Django 的 MVT 架构和 ORM 是怎么落地的2.1 为什么选 Django 这套技术栈这个项目用的是 Python 最主流的 Web 框架 Django原因是舆情分析系统的典型场景——数据采集入库、后台管理、统计展示——几乎都是 CRUD 加聚合查询Django 自带的 Admin 后台和 ORM 能省掉一大半重复劳动。项目里能看到 layui.css、admin.css 这类文件说明前端管理页面用的是 Layui 这套轻量 UI 框架搭配 Django 模板引擎渲染不需要单独写前端工程。Django 的 MVT 架构在项目里分得很清楚Model 层负责舆情数据表的映射View 层接收请求、处理业务逻辑Template 层渲染页面。实际读源码的时候建议按这个顺序走先看 models.py 里的表结构再看 views.py 里的业务函数最后看 urls.py 里的路由配置这样整条链路很快就通了。2.2 数据库模型设计舆情数据表怎么建项目提供数据库脚本但先看 models.py 更能理解表结构的设计意图。舆情分析系统的核心表通常包含舆情信息表字段一般有标题、来源、发布时间、正文内容、情感标签、采集时间等。用 Django 的 ORM 定义大致长这样# models.py from django.db import models class NewsInfo(models.Model): title models.CharField(max_length200, verbose_name标题) source models.CharField(max_length50, verbose_name来源) content models.TextField(verbose_name正文内容) publish_time models.DateTimeField(verbose_name发布时间) sentiment models.IntegerField( choices[(1, 正面), (0, 中性), (-1, 负面)], default0, verbose_name情感标签 ) keyword models.CharField(max_length20, blankTrue, verbose_name关键词) created_at models.DateTimeField(auto_now_addTrue, verbose_name入库时间) class Meta: db_table news_info verbose_name 舆情信息 verbose_name_plural verbose_name def __str__(self): return self.title这里的 sentiment 字段是整个舆情分析的核心——用整数存情感倾向而不是直接用字符串好处是后续做统计聚合时可以直接按数值分组GROUP BY sentiment一条 SQL 就能出饼图数据。created_at 设置成自动写入保证入库时间不用手动维护。2.3 数据库脚本导入MySQL 建库建表全流程项目里附带的数据库脚本通常是.sql文件对应 MySQL 的建库建表语句。导入时先建库再导表不要直接在默认库跑不然表名冲突很难排查。常见操作是mysql -u root -p CREATE DATABASE yuqing DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE yuqing; SOURCE /path/to/yuqing.sql;注意字符集务必选utf8mb4而不是utf8。舆情数据经常带 emoji 表情或特殊符号utf8在 MySQL 里是 3 字节存 emoji 直接报错utf8mb4是 4 字节能完整存下来。这个项目里如果导入脚本后页面打开乱码九成是字符集问题后面避坑章节还会细说。3. 核心功能拆解舆情数据怎么从入库到展示3.1 数据采集入库ORM 批量操作与去重策略舆情分析系统的第一环是数据进来。源码里在 views.py 或独立的 utils.py 里通常有一段数据采集入库的逻辑不管是爬虫抓取还是手工导入最终都要落到 ORM 写入。批量写入时优先用bulk_create逐条save()在数据量上来后会非常慢# utils.py from .models import NewsInfo def batch_save_news(news_list): news_list: [{title: ..., source: ..., content: ..., publish_time: 2025-01-01 10:00:00}, ...] objs [] for item in news_list: # 按标题 发布时间判断是否已存在避免重复入库 if NewsInfo.objects.filter(titleitem[title], publish_timeitem[publish_time]).exists(): continue objs.append(NewsInfo(**item)) if objs: NewsInfo.objects.bulk_create(objs, batch_size500) return len(objs)batch_size500是分批写入的大小一般建议 200 到 1000 之间太大容易超过 MySQL 的 max_allowed_packet 限制太小又会增加数据库交互次数。去重用title publish_time两个字段组合判断比单独用标题可靠因为不同来源转载时标题可能相同但发布时间不同。3.2 情感分析实现基于情感词典的判别逻辑舆情系统的关键输出是情感标签。源码里的实现方式通常不是调用大模型接口而是用情感词典打分——维护一个正面词表和一个负面词表对正文分词后在词表里匹配打分。这种方式的好处是离线可跑、解释性强也适合毕设答辩时讲清楚原理# sentiment.py import jieba POSITIVE_WORDS set([支持, 满意, 优秀, 点赞, 利好, 提升]) NEGATIVE_WORDS set([反对, 投诉, 失望, 严重, 违规, 下跌]) def analyze_sentiment(text): words jieba.lcut(text) score 0 for w in words: if w in POSITIVE_WORDS: score 1 elif w in NEGATIVE_WORDS: score - 1 if score 0: return 1 # 正向 elif score 0: return -1 # 负向 return 0 # 中性这个方法看着简单但有几个坑必须说明白。分词结果直接影响情感打分——比如“不支持”会被 jieba 切成“不”和“支持”“支持”是正面的但整体语义是负面的词典法天然处理不了否定前缀。一个妥协做法是把“不”“无”“没”等否定词纳进来如果在情感词前 2 个词内出现权重反转。源码里如果没做这层处理改造时优先级最高因为否定场景在舆情数据里太常见了。3.3 可视化与统计页面ORM 聚合查询做数据图表后台管理页面展示舆情趋势时用的是 Django ORM 的聚合查询。按天统计舆情数量、按情感类型分组都是这类系统的标配需求。用django.db.models的Count能直接出结果# views.py from django.db.models import Count from django.shortcuts import render from .models import NewsInfo from django.utils import timezone from datetime import timedelta def dashboard(request): # 最近 7 天每日舆情数量 end_date timezone.now() start_date end_date - timedelta(days6) daily_data (NewsInfo.objects .filter(created_at__range[start_date, end_date]) .extra({day: DATE_FORMAT(created_at, %%Y-%%m-%%d)}) .values(day) .annotate(totalCount(id)) .order_by(day)) # 情感类型分布 sentiment_data (NewsInfo.objects .values(sentiment) .annotate(totalCount(id)) .order_by(sentiment)) return render(request, admin/dashboard.html, { daily_data: list(daily_data), sentiment_data: list(sentiment_data), })这里的extra里面用了DATE_FORMAT函数在 MySQL 下可以把created_at按天格式化后分组。注意%%Y是 Django 转义后的写法直接在 MySQL 里是%Y写错一个百分号日期分组就查不出数据。这个统计结果可以直接喂给前端图表库Layui 自带的layui.use(table)或 ECharts 都行数据格式统一成[{day: 2025-06-01, total: 12}]这样的列表就能渲染。4. 参数与运行配置让项目在自己的环境里跑起来4.1 Django 项目配置与启动流程拿到源码后第一步不是直接runserver而是先检查 settings.py 里的环境配置。数据库连接、静态文件路径、时区这三个地方是九成运行报错的重灾区。一个典型的配置长这样# settings.py DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: yuqing, # 数据库名 USER: root, # 数据库用户 PASSWORD: 123456, # 改成自己的密码 HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } } TIME_ZONE Asia/Shanghai USE_TZ True STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]OPTIONS里强制指定utf8mb4是防止读取时中文变乱码的关键。USE_TZ True加上TIME_ZONE Asia/Shanghai的组合会让 Django 在数据库里存 UTC 时间、展示时转本地时间查询时用datetime.now()还是会出偏差——上面统计代码里我用timezone.now()就是因为这个。启动前先迁移数据库再创建管理员账号pip install django pymysql jieba mysqlclient python manage.py makemigrations python manage.py migrate python manage.py createsuperuser python manage.py runserver 0.0.0.0:8000pymysql 和 mysqlclient 只需要装一个。通常 Django 连 MySQL 优先装 mysqlclient但它在 Windows 上常常装不上这时改用 pymysql并在__init__.py里写pymysql.install_as_MySQLdb()兼容这个操作是源码里比较常见的处理方式。4.2 舆情分析参数调节情感词典与采集频率情感词典的维护直接影响分析结果的准确率。源码自带的情感词典不可能覆盖所有领域词汇通用词表在金融、教育、医疗等垂直场景下准确率会明显下降。实际操作中我会把词典独立成一个words.txt文件按行存放每次从后台管理页面追加词汇不重新部署代码# 添加词汇后格式每行一个词 好评 值得推荐 体验不错 虚假宣传 问题严重采集频率和分页参数在视图层控制。页面列表展示用 Django 的分页器每页条数和最大展示范围都能调# views.py from django.core.paginator import Paginator def news_list(request): page request.GET.get(page, 1) keyword request.GET.get(keyword, ) news_qs NewsInfo.objects.order_by(-publish_time) if keyword: news_qs news_qs.filter(title__icontainskeyword) paginator Paginator(news_qs, 10) # 每页 10 条 page_obj paginator.get_page(page) return render(request, admin/news_list.html, {page_obj: page_obj})分页器参数有讲究Paginator(news_qs, 10)里的 10 是每页条数数据量大时不要设置超过 50否则页面渲染会明显卡顿get_page(page)比page(page)好在页码越界时不会抛异常自动返回最后一页。5. 常见问题与避坑复现这套系统我踩过的坑5.1 数据库脚本导入时报错“Unknown collation”现象执行SOURCE yuqing.sql时报错提示Unknown collation: utf8mb4_0900_ai_ci。原因项目是在 MySQL 8.0 环境下导出的数据库脚本默认排序规则是utf8mb4_0900_ai_ci但你本地用的可能是 MySQL 5.7 或更低版本不支持这个排序规则。解决用文本编辑器打开 SQL 文件全局替换utf8mb4_0900_ai_ci为utf8mb4_general_ci保存后再执行导入。这是一个很常见的兼容性问题适合在毕设文档的“运行环境”部分特别标注出来。5.2 Django 管理后台登录后样式丢失现象访问/admin/后台能登录但页面全是纯文本CSS 样式完全没加载。原因Django 的开发服务器在 DEBUG 模式下不会自动处理 Admin 自带的静态文件需要配置STATIC_ROOT并执行collectstatic或者直接在 settings.py 里把STATICFILES_DIRS指向 Django 源码的 admin 静态目录——但后者不优雅部署时必踩。解决做一次静态文件收集python manage.py collectstatic然后把 settings.py 里的STATIC_ROOT指向一个真实存在的目录比如项目根目录下的static_collected。如果还在开发阶段也可以直接把django.contrib.admin的静态文件路径手动加进STATICFILES_DIRS但这种方法只适合临时调试。5.3 中文乱码与 emoji 存储失败现象舆情正文里有 emoji 表情时写入数据库报错或者保存后读出来全是???。原因数据库表字符集不是utf8mb4或者 Django 连接 MySQL 时没有指定 charset。MySQL 的小于 5.5.3 的版本完全不支持 4 字节 UTF-8 字符5.5 需要显式使用utf8mb4。解决两步走。第一步改表ALTER DATABASE yuqing CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ALTER TABLE news_info CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第二步在 Django 的数据库配置OPTIONS里加charset: utf8mb4然后重启服务。这套组合下来emoji 和生僻字都能正常存取。5.4 bulk_create 大批量插入时主键冲突现象第一次批量导入数据成功第二次跑同样的采集脚本时报主键重复部分数据没写进去。原因bulk_create遇到重复主键默认是整体报错而不是跳过。如果表的主键用的是自增 id第二次导入时 id 不会重复但项目如果把 title 设置成了唯一索引就会撞上。解决批量导入前先做一次去重查询在 Python 层面过滤掉已存在的记录或者直接改用INSERT IGNORE语义——Django 的bulk_create支持ignore_conflictsTrue参数NewsInfo.objects.bulk_create(objs, batch_size500, ignore_conflictsTrue)注意ignore_conflictsTrue是在 MySQL 5.7 才生效并且它会忽略所有冲突不只会忽略主键冲突其他约束冲突也会被静默吞掉所以最好仍然在业务端先做去重。5.5 分页点击第 2 页后筛选条件丢失现象搜索关键词后点击第 2 页列表又变回全部数据筛选条件没了。原因分页链接只生成了?page2没有把keyword参数拼接进去。这是 Django 分页最常见的翻车点。解决在模板里生成分页链接时带上原来查询参数# views.py 里拿到当前请求的完整 query dict去掉 page 再传给模板 base_query request.GET.copy() if page in base_query: del base_query[page] page_base base_query.urlencode() # keywordxxx模板里分页链接写成?{{ page_base }}page{{ page_obj.next_page_number }}这样第 2 页依然带着关键词。这个细节没处理好的话在答辩演示时很容易被问住。6. 进阶验证技巧用一条命令检查整条链路是否跑通项目改完、服务跑起来之后很多人的验证方式是打开浏览器点一圈页面觉得界面能显示就算成功。但后端的数据链路有没有真通光看页面是不够的——页面可能读取的是缓存或者前端写死的假数据。我会用 Django 的 shell 直接验证核心链路比如批量入库 10 条测试数据后立即查统计结果python manage.py shell -c from datetime import timedelta from django.utils import timezone from newsapp.models import NewsInfo # 清空测试数据 NewsInfo.objects.all().delete() # 造 10 条数据带不同情感标签 for i in range(10): NewsInfo.objects.create( titlef测试舆情{i}, sourcetest, content这是一个用于验证系统的测试文本, publish_timetimezone.now() - timedelta(hoursi), sentimenti % 3 - 1 ) # 验证统计查询 from django.db.models import Count print(总数:, NewsInfo.objects.count()) print(按情感分组:, list(NewsInfo.objects.values(sentiment).annotate(cCount(id)))) 输出里能看到总数是 10按情感分组有三组数字如果分组结果符合预期说明从 ORM 到数据库再到统计聚合这条路是通的。页面展示最多只是把这段数据渲染成了图表数据源没问题页面基本不会出大错。从那以后我每次调完采集脚本都会用这种 shell 直查的方式先验证数据链路再开页面看效果省掉了一大半浏览器刷新排查的时间。另外一个值得做的小技巧是打开 Django 的 SQL 日志观察 ORM 生成的 SQL 是否合理。在 settings.py 里临时加一段日志配置LOGGING { version: 1, handlers: {console: {class: logging.StreamHandler}}, loggers: {django.db.backends: {handlers: [console], level: DEBUG}}, }然后跑任何页面终端里会打印出所有实际执行 SQL。看到SELECT * FROM news_info这种不带条件的全表扫描出现在列表页时就该给 view 加过滤条件或分页了看到 1 秒内重复执行两三遍一模一样的 SQL就说明有 N1 查询问题需要用select_related或prefetch_related优化。舆情分析系统的数据量一上来这两个优化点直接决定后台页面会不会卡死。这套项目源码里的核心链路——采集入库、情感分析、聚合统计——全部拆开验证过之后剩下的就是在自己业务方向上做扩展了。希望这份拆解笔记能帮你把项目真正跑通也帮你在毕设答辩或项目复盘时能清楚地讲出每一步背后的原理和坑。本文还有配套的精品资源点击获取