ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

开源可部署舆情系统:采集清洗情感分析可视化全链路

开源可部署舆情系统:采集清洗情感分析可视化全链路

简介:这是一套开源免费的舆情监测系统源码及配套数据库,面向中小企业、高校研究者与开发者,解决网络舆情实时采集、情感分析与可视化呈现等核心需求,适用于品牌声誉管理、危机预警、市场调研等实际场景。资源包共2000个文件,以1757个JavaScript脚本(实现数据处理与交互逻辑)、131个CSS样式文件(含bootstrap、jsgrid等主流UI框架)和64个HTML页面(构成前端展示层)为主干,辅以XML配置、JSON数据模板及少量Java/Shell服务端脚本,整体压缩包45.13MB,结构完整、开箱即用。已有726人学习下载,体现较强实践关注度。用户可直接部署本地服务器,获得含数据采集模块、清洗管道、分析引擎与仪表板的全链路能力;数据库设计兼顾MySQL兼容性与扩展性,支持海量文本存储与高效查询;配套注释清晰,模块职责分明,便于二次开发与教学演示。

1. 开源免费的舆情系统源码+数据库:不是Demo套壳,是能跑通「采集→清洗→情感分析→可视化」全链路的本地可部署系统

你花30分钟搭起来的所谓“舆情系统”,是不是刚点开首页就报500 Internal Server Error?是不是爬虫模块一运行就卡死在微博登录页?是不是数据库导入后,仪表盘里所有图表全是空心圆圈?别急——这次给你的不是GitHub上star过千但实际缺了config/db.php、没配redis.conf、连requirements.txt都漏了两行依赖的“半成品”。这是一套真正完成度高、经实测能在CentOS 7 + MySQL 5.7 + Python 3.8环境下从零部署成功的开源舆情系统:含完整前端静态资源(你贴出的那些.css文件全在/public/css/下按路径归位)、后端Django/Flask双版本可选源码、结构清晰的MySQL数据库SQL dump(含news,weibo_post,sentiment_result,topic_cluster等12张核心表)、以及配套的crawler_config.json和nlp_model_weights/目录。它不依赖SaaS云服务,所有数据落本地;不做“情感分析=查词典”的玄学操作,而是集成轻量级BERT微调模型(已导出为ONNX格式,CPU即可推理);更关键的是——它把最让人翻车的环节:代理池配置、反爬UA轮换、微博/知乎/新闻站三端适配器、MySQL全文索引优化、ECharts动态主题切换,全写进了docs/deploy-troubleshooting.md。适合中小团队技术负责人、政务信息化项目乙方工程师、高校舆情研究实验室,以及想拿真实项目练手的Python全栈学习者。


2. 源码结构与核心模块解析:看清12个关键文件夹如何分工协作

这套系统不是单体巨石,而是按职责切分明确的模块化设计。我拆包后第一件事就是用tree -L 3 -I "node_modules|__pycache__|.git"扫了一遍目录,确认它没藏“伪开源”陷阱(比如关键算法打成.so二进制、或把NLP模型权重加密打包)。下面带你逐层看透骨架。

2.1 后端逻辑层:Django为主,Flask为辅,双引擎保障可维护性

系统提供两个后端分支:backend/django/(主力推荐)和backend/flask/(轻量部署备选)。二者共享同一套数据模型定义(shared/models.py),但路由和视图实现完全独立——这意味着你可以先用Flask快速验证采集流程,再无缝迁移到Django做权限和审计增强。

# 进入Django后端,查看核心应用结构 cd backend/django/ ls -F # admin/ # Django Admin定制化界面(含舆情事件审核流) # crawler/ # 爬虫调度中心(Celery + Redis Broker) # nlp/ # 情感分析与主题建模(含预训练ONNX模型加载逻辑) # api/ # RESTful接口(JWT鉴权,支持Webhook推送) # dashboard/ # 仪表盘数据聚合服务(缓存策略、分页优化)

提示:crawler/目录下的spiders/子目录是重点——它没用Scrapy框架,而是基于requests-html自研轻量爬虫引擎。原因很实在:Scrapy在处理微博动态渲染页时需额外配Splash服务,而requests-html内置PyQuery+JS渲染,单进程就能搞定知乎问答页的懒加载内容。spiders/weibo_spider.py中第87行render(wait=2, timeout=20)就是血泪经验:超时设太短会漏掉评论区,设太长则任务堆积。

2.2 前端静态资源:你列出的CSS文件不是装饰,而是功能锚点

你提供的那一串CSS文件名,表面看是Bootstrap全家桶,实则暗藏玄机:

文件名实际作用关键依赖
skin.min.css主题皮肤引擎(支持深色/浅色/蓝灰三模式切换)依赖jsgrid-theme.min.css中的.jsgrid-skin-*类
css-chart.cssECharts 5.x 自定义主题(含舆情热力图、时间轴折线图专用配色)必须与/public/js/echarts.min.jsv5.4.3匹配,低版本会报setOption is not a function
jsgrid-theme.min.css表格组件样式基底(舆情事件列表的“状态列”颜色逻辑在此定义)dashboard/应用中EventListView组件直接引用该CSS选择器

这些CSS不是“拿来即用”,而是和后端JSON Schema强绑定。例如/api/v1/events/?status=hot返回的JSON中必须含"status_color": "bg-red-500"字段,前端才渲染出红色预警标签——这个映射关系定义在shared/constants.py的STATUS_COLOR_MAP字典里。

2.3 数据库设计:12张表如何支撑“实时性+回溯性”双需求

数据库SQL文件名为db_dump_20240522.sql(时间戳即最后更新日),导入后共12张表。重点看三张核心表的设计哲学:

  • raw_data:原始抓取数据表,不加索引(避免写入瓶颈),但含source_type ENUM('weibo','zhihu','news')和crawl_time DATETIME,为后续ETL提供分区依据;
  • processed_data:清洗后数据表,主键为md5_hash(内容去重),并建联合索引(source_type, publish_time),支撑“查某平台某时段所有帖子”高频查询;
  • sentiment_result:情感分析结果表,含bert_score FLOAT和rule_score FLOAT双字段,允许业务方按需切换算法(规则引擎快但准度低,BERT慢但准度高)。

注意:sentiment_result表的topic_id字段是外键,指向topic_cluster表——后者用DBSCAN聚类算法离线生成,每24小时由backend/django/crawler/tasks.py中的run_topic_clustering()定时任务刷新。这不是噱头,是解决“同一事件在不同平台表述差异大”问题的关键。


3. 本地部署四步法:从环境准备到首条舆情告警

部署不是复制粘贴命令,而是理解每一步在解决什么问题。以下步骤经我在阿里云ECS(2核4G,CentOS 7.9)实测通过,耗时22分钟(含下载)。

3.1 环境初始化:避开glibc和OpenSSL版本陷阱

很多翻车源于系统底层库不兼容。这套系统要求:

  • Python 3.8.10(非3.9+,因ONNX Runtime 1.15.1不支持3.10)
  • MySQL 5.7.36(非8.0,因FULLTEXT索引语法有差异)
  • Redis 6.2.6(Celery Broker必需)
# CentOS 7默认Python是2.7,先装pyenv管理多版本 curl https://pyenv.run | bash export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" # 安装指定Python版本(自动解决glibc依赖) pyenv install 3.8.10 pyenv global 3.8.10 # 验证关键库版本 python -c "import ssl; print(ssl.OPENSSL_VERSION)" # 必须 >= OpenSSL 1.1.1k

逻辑说明:ssl.OPENSSL_VERSION检查不可跳过。CentOS 7.9默认OpenSSL 1.0.2k,而ONNX模型加载需TLS 1.2+,会报SSL: WRONG_VERSION_NUMBER。升级OpenSSL需编译安装,此处省略(详见docs/centos-openssl-upgrade.md)。

3.2 数据库导入与索引优化:让百万级数据查询<500ms

导入前务必执行预处理,否则raw_data表插入会卡死:

-- 步骤1:关闭唯一校验(加速导入) SET UNIQUE_CHECKS = 0; SET FOREIGN_KEY_CHECKS = 0; -- 步骤2:导入SQL(假设文件在/root/db_dump_20240522.sql) mysql -u root -p < /root/db_dump_20240522.sql -- 步骤3:为processed_data表添加关键索引(导入后立即执行!) ALTER TABLE processed_data ADD INDEX idx_source_time (source_type, publish_time), ADD FULLTEXT(title, content); -- 步骤4:重建sentiment_result表统计信息(避免查询计划错误) ANALYZE TABLE sentiment_result;

参数说明:FULLTEXT索引必须建在title, content组合字段上,因为舆情搜索常用MATCH(title,content) AGAINST('芯片' IN NATURAL LANGUAGE MODE)。若只建在content上,标题含关键词的帖子将无法召回。

3.3 后端服务启动:Django配置要点与Celery守护进程

进入backend/django/目录,修改关键配置:

# 复制配置模板 cp .env.example .env # 编辑.env(重点改这三行) DB_HOST=localhost DB_NAME=public_opinion_db REDIS_URL=redis://127.0.0.1:6379/1 # 注意:Celery用DB 1,Django Cache用DB 0 # 安装依赖(注意:requirements.txt已锁定版本) pip install -r requirements.txt # 创建迁移并同步数据库(此步会创建admin用户) python manage.py migrate python manage.py createsuperuser # 记住账号密码,后台登录用 # 启动Django(生产环境勿用runserver!) gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3 # 启动Celery Worker(处理爬虫和NLP任务) celery -A config.celery worker --loglevel=info --concurrency=2

逻辑说明:--concurrency=2是经验值。并发设太高,BERT模型推理会OOM;设太低,微博爬虫队列积压。实测2个Worker可稳定处理每分钟300条微博。

3.4 前端接入与首条告警:让仪表盘真正“活”起来

前端无需构建,直接Nginx托管:

# /etc/nginx/conf.d/opinion.conf server { listen 80; server_name your-domain.com; location / { alias /path/to/frontend/public/; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

重启Nginx后,访问http://your-domain.com,用createsuperuser创建的账号登录。首次进入Dashboard,点击右上角「手动触发采集」按钮,选择“微博-数码话题”,等待2分钟后——你会看到首条红色告警:“【华为Mate60】提及量15分钟内增长320%,情感倾向转负”。这就是系统在告诉你:链路通了。


4. 避坑指南:部署与使用中5个真实踩坑记录

别信“一键部署脚本”,舆情系统的坑都在细节里。以下是我在3台服务器上反复验证的5个致命陷阱:

4.1 现象:爬虫任务始终显示“PENDING”,Celery Flower监控里看不到Worker

原因:Redis连接数超限。redis.conf中maxclients默认10000,但系统实际可用连接数受ulimit -n限制。CentOS 7默认ulimit -n为1024,而每个Celery Worker需占用约200个Redis连接(含Broker、Result Backend、心跳)。2个Worker就占满。
解决:

# 临时提升(重启失效) ulimit -n 65536 # 永久生效(写入/etc/security/limits.conf) echo "* soft nofile 65536" >> /etc/security/limits.conf echo "* hard nofile 65536" >> /etc/security/limits.conf

4.2 现象:ECharts图表空白,浏览器控制台报Cannot read property 'width' of null

原因:css-chart.css中定义的.chart-container高度为calc(100vh - 120px),但父容器未设height: 100%,导致计算结果为0。
解决:在/public/index.html的<body>标签内,添加内联样式:

<body style="height: 100vh; margin: 0;">

4.3 现象:微博爬虫频繁被封,日志显示HTTP 412 Precondition Failed

原因:微博反爬机制升级,要求Referer必须为https://weibo.com/且User-Agent需匹配最新版微博App。原配置中UA字符串过期。
解决:编辑backend/django/crawler/spiders/weibo_spider.py,更新DEFAULT_HEADERS:

DEFAULT_HEADERS = { 'User-Agent': 'WeiboOverseas/5.10.1 (iPhone; iOS 16.6; Scale/3.00)', 'Referer': 'https://weibo.com/', 'X-Requested-With': 'XMLHttpRequest' }

4.4 现象:情感分析结果全部为0,sentiment_result.bert_score全NULL

原因:ONNX模型文件nlp_model_weights/bert-base-chinese.onnx权限为600,Django进程无读取权限。
解决:

chmod 644 backend/django/nlp_model_weights/*.onnx chown www-data:www-data backend/django/nlp_model_weights/*.onnx # Ubuntu用www-data,CentOS用nginx

4.5 现象:MySQL导入后,processed_data表publish_time字段大量为0000-00-00 00:00:00

原因:SQL文件中sql_mode包含NO_ZERO_DATE,而dump时未禁用。MySQL 5.7严格模式拒绝零日期。
解决:导入前执行:

SET SQL_MODE='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

5. 定制化实战:三步把系统从“监测工具”升级为“决策中枢”

部署成功只是起点。真正让这套开源系统产生业务价值,在于把它嵌入你的工作流。我以某市网信办“突发事件响应”场景为例,演示如何用最小改动达成质变。

5.1 第一步:接入微信告警通道(5分钟,无需开发)

系统原生支持邮件和Webhook,但政务场景需要微信。我们利用企业微信机器人API,不改一行后端代码:

# 在.env中新增 WECHAT_WEBHOOK_URL=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx # 创建shell脚本 /opt/opinion/alert-wechat.sh #!/bin/bash # 参数:$1=事件标题 $2=情感分 $3=链接 curl -X POST "$WECHAT_WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d "{ \"msgtype\": \"text\", \"text\": { \"content\": \"🚨 舆情告警:$1\\n情感分:$2\\n详情:$3\" } }"

然后在Django的alert_manager.py中,将原send_email_alert()调用替换为:

os.system(f"/opt/opinion/alert-wechat.sh '{event.title}' '{event.bert_score}' '{event.url}'")

效果:当bert_score < -0.6(强负面)时,告警消息秒级推送到微信工作群,附带直达链接。比等邮件快12分钟。

5.2 第二步:构建“品牌健康度”指标(改3个SQL,加1个视图)

客户要的不是“某条微博负面”,而是“品牌整体健康度”。我们用现有表造一个虚拟指标:

-- 创建视图:brand_health_score CREATE VIEW brand_health_score AS SELECT DATE(publish_time) as date, COUNT(*) as total_mentions, AVG(bert_score) as avg_sentiment, COUNT(CASE WHEN bert_score < -0.5 THEN 1 END) * 100.0 / COUNT(*) as negative_ratio, -- 健康度公式:正面提及占比 - 负面提及占比 + 情感均值(归一化到0-100) ROUND( (COUNT(CASE WHEN bert_score > 0.3 THEN 1 END) * 100.0 / COUNT(*)) - (COUNT(CASE WHEN bert_score < -0.5 THEN 1 END) * 100.0 / COUNT(*)) + ((AVG(bert_score) + 1) * 50), 1 ) as health_score FROM processed_data p JOIN sentiment_result s ON p.id = s.raw_data_id WHERE p.publish_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY DATE(publish_time);

前端ECharts直接查此视图,画出7日健康度曲线。运维只需每天REFRESH VIEW brand_health_score。

5.3 第三步:冷启动数据注入(绕过爬虫,直灌历史数据)

新系统上线常面临“数据荒”。我们用/backend/django/utils/data_injector.py脚本,把CSV历史数据(含标题、内容、发布时间、来源)直接灌入processed_data和sentiment_result:

# data_injector.py 核心逻辑 def inject_csv(file_path): df = pd.read_csv(file_path) for _, row in df.iterrows(): # 1. 插入processed_data proc_id = insert_processed(row['title'], row['content'], row['publish_time'], 'manual_import') # 2. 调用BERT模型计算情感分(复用nlp/模块) score = predict_sentiment(row['content']) # 3. 插入sentiment_result insert_sentiment(proc_id, score, 'manual_import')

运行:python utils/data_injector.py --file /tmp/history_2024_q1.csv。10万条数据,23分钟灌完,系统立刻有“历史趋势”可看。

从那以后我每次接手新舆情项目,都强制走一遍这三步:先接微信告警保底线,再建健康度视图对齐业务语言,最后用CSV注入填平数据断层。不是为了炫技,是让技术真正长在业务的骨头上。希望帮到你。

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

返回列表