综合设计——多源异构数据采集与融合应用综合实践

一.基本信息

这个项目属于哪个课程 https://edu.cnblogs.com/campus/fzu/2024DataCollectionandFusiontechnology
组名、项目简介 组名:福小兵,项目需求:实时舆情监控系统,项目目标:为福州大学提供舆情监控与决策辅助工具,技术路线:使用 Flask 后端、Memfire(PostgreSQL)数据库和 Vue 前端技术栈,建立从数据采集到情感分析再到可视化的完整系统
团队成员学号 102202141黄昕怡,102202101马鑫,1022021045谢含,102202112刘莹,102202126陈家凯,102202106王强,102202124阿依娜孜·赛日克,102202153来在提’叶鲁别克
这个项目目标 多模态数据采集,话题总结,智慧情绪分析,智能AI舆情分析
其他参考文献 [1]马茜, 谷峪, 张天成, & 于戈. (2013). 一种基于数据质量的异构多源多模态感知数据获取方法. 计算机学报, 36(10), 12. [2]司俊勇, & 付永华. (2024). 多模态数据融合的在线学习情感计算研究. 图书与情报(3), 69-80. [3]何炎祥, 孙松涛, 牛菲菲, & 李飞. (2017). 用于微博情感分析的一种情感语义增强的深度学习模型. 计算机学报, 40(4), 18. [4] 张玲, 王磊, & 李铭. (2021). 面向社交媒体情感分析的多模态数据融合方法研究. 图书情报工作, 65(8), 21-28. [5]杨宇恒.基于跨模态预测融合的多模态情感识别方法研究[D].西安理工大学,2024.DOI:10.27398/d.cnki.gxalu.2024.000099.
Github代码链接 https://github.com/fufubuff/fu_police
Gitee代码链接 <https://gitee.com/wang-qiangsy/crawl_project/tree/master/福小兵-综合实验 >

二.整体项目

1.项目名称

福小兵:情感分析及舆情分析平台

2.项目背景

随着互联网的飞速发展和社交媒体的广泛普及,网络舆情已经成为影响高校形象和声誉的关键因素。各种社交媒体平台、论坛和新闻网站为公众提供了发表观点和分享信息的渠道,使得信息传播的速度和范围空前扩大。福州大学作为福建省重点建设的高水平大学,其在网络上的公众形象不仅关系到学校在福建省内的地位,更直接影响到其在全国乃至国际上的知名度和美誉度。于是本项目面向福州大学,聚焦其在微博、贴吧、知乎等平台的多模态网络舆情数据,以 Flask 后端、Memfire(PostgreSQL) 数据库和 Vue 前端 为主要技术栈,搭建一个从数据采集到情感分析再到可视化展示的完整系统,为学校舆情监控与管理提供支持和决策辅助。

3.项目内容

多模态数据采集

  • 爬取福州大学在各大网站(如微博、贴吧、知乎等)上的评论、帖子内容和评论图片。
  • 建立一个全面的舆情数据库,涵盖多平台、多媒体形式的数据。

话题总结

  • 分析网络社区中关于福州大学的热点话题和关注点。
  • 提取高频关键词,了解公众最关心的问题。

智慧情绪分析

  • 使用多种深度学习模型,对文本和图片数据进行情感倾向分析(积极、中立、消极)。
  • 监测公共对福州大学相关的情绪变化趋势,识别潜在的舆情风险。

智能AI舆情分析

  • 调用大语言模型的接口(如GPT-4),提供深入的AI分析和预测,对学校的舆论概况进行智能监控。
  • 生成自动化的舆情报告,辅助决策。

学校保护与归属

  • 通过系统的建立,表达对福州大学的归属感和保护欲望。
  • 帮助学校及时监控舆情,维护良好的公众形象。

4.项目架构

前端层(Vue,HTML+CSS+js)

  • 前端一包括用户自爬取数据和报告式分析使用 Vue 搭建前端页面,包含搜索输入框、情感分析可视化、AI 总结输出等界面。
  • 前端二包含爬取内容的数据库的数据展示,方式有柱形图,饼图,词云图,分页表格,还包含音乐播放器,日历。此外还有ai聊天窗口可让用户对舆情数据进行了解。
  • 以上通过对接后端api,获取处理数据进行数据可视化。

后端层(Flask)

  • 后端api接口包括:
    数据获取:在数据库中获取相关内容。
    情感分析:基于深度学习模型,对文本做多类别情感识别。
    数据可视化:情感分析进行统计总结,通过各种图标,不限于柱形图词云图进行可视化。
  • 整合大语言模型接口,用于对检索和情感分析结果进行二次生成与深入总结。

数据爬取

  • 利用爬虫技术从微博、贴吧等平台抓取多模态内容。
  • 将抓取的数据上传到云端的PostgreSQL数据库内。

5.项目展示

  • 爬取内容的数据库的数据展示,方式有柱形图,饼图,词云图,分页表格,还包含音乐播放器,日历。此外还有ai聊天窗口可让用户对舆情数据进行了解。

  • 用户自爬取数据和报告式分析

三.个人工作

1.爬取内容的数据库的数据展示页面前端设计

  • 以图片数据的数据展示为例,展示部分代码
<header>
        <h1>福卫兵is all you need</h1>
        <h1>福卫兵出击</h1>
    </header>

   <main>
    <!-- Update the HTML in templates/data.html -->
    <div class="chart-container">
        <canvas id="emotionChart"></canvas>
        <div id="wordCloud" style="display: none;">
            <p class="word">Fzuer都说了什么呢</p>
            <img src="img/wordcloud.png" alt="Word Cloud" class="wordcloud">
        </div>
        <div class="chart-pagination">
            <button id="prevChart" title="上一页">
                <img src="img/back-icon.png" alt="上一页">
            </button>
            <button id="nextChart" title="下一页">
                <img src="img/skip.png" alt="下一页">
            </button>
        </div>
    </div>

    <div class="left-sidebar">
    <div class="ai-window">
    <div id="chatContainer"></div>
    <input type="text" id="userInput" placeholder="福兵,开启今日的巡查吧!" onkeypress="handleKeyPress(event)">
</div>
<div class="button-container">
    <img src="img/fu.png" alt="Clickable Image" class="clickable-image" onclick="sendText()">
</div>
</div>

    <div class="right-sidebar">
        <!-- 音乐播放器 -->
        <div class="player">
            <!-- 歌曲封面 -->
            <div class="cover">
                <img src="music/back.jpg" alt="" class="music-back">
                <div class="song">
                    <p>
                    纯音乐,请欣赏<br>
                    </p>
                </div>
            </div>
            <!-- 歌词设置 -->
            <h2>清澈的眼睛</h2>
            <!-- audio标签 -->
            <div class="mus">
                <audio src="music/bgm.mp3" controls autoplay loop></audio>
            </div>
        </div>
        <div class="calendar-container">
    <div id="calendar"></div>
</div>
    </div>

    <table>
        <thead>
            <tr>
                <th>序号</th>
                <th>评论图片</th>
                <th>图片文字分析</th>
                <th>情感分析</th>
            </tr>
        </thead>
        <tbody id="commentsTableBody"></tbody>
    </table>

    <div class="pagination">
        <button id="prevPage" disabled>上一页</button>
        <span id="pageInfo"></span>
        <button id="nextPage">下一页</button>
        <input type=" number" id="jumpToPage" min="1" placeholder="页码">
        <button id="jumpPageButton">跳转</button>
    </div>
</main>
  • 数据展示(饼图)
new Chart(ctx, {
                    type: 'pie',
                    data: {
                        labels: ['愤怒', '厌恶', '恐惧', '高兴', '悲伤', '惊讶', '中性'],
                        datasets: [{
                            data: [
                                emotionData.angry || 0,
                                emotionData.disgust || 0,
                                emotionData.fear || 0,
                                emotionData.happy || 0,
                                emotionData.sad || 0,
                                emotionData.surprise || 0,
                                emotionData.neutral || 0
                            ],
                            backgroundColor: [
                                '#c0392b',
                                '#a93226',
                                '#e67e22',
                                '#f1c40f',
                                '#3498db',
                                '#9b59b6',
                                '#95a5a6'
                            ],
                            hoverBackgroundColor: [
                                '#c0392b',
                                '#a93226',
                                '#e67e22',
                                '#f1c40f',
                                '#3498db',
                                '#9b59b6',
                                '#95a5a6'
                            ]
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                display: true,
                                position: 'bottom'
                            }
                        }
                    }
                });

                // 点击图片打开新标签页
                row.querySelector('img').addEventListener('click', function () {
                    window.open(this.src, '_blank');
                });
            } 

2.实现后端数据库获取数据

api

def get_db_connection():
    try:
        conn = psycopg2.connect(
            host='101.132.80.183',
            port='5433',
            database='dbf72a0c3d7d054ef39b98488f2995d159zz',
            user='zzsthere',
            password='20020925Aa'
        )
        return conn
    except Exception as e:
        print(f"数据库连接错误: {e}")
        return None
elif 'merged_images' in query:
                # 处理 merged_images 表数据
                sentiment_data = row[3]
                try:
                    sentiment = json.loads(sentiment_data)
                except (TypeError, json.JSONDecodeError):
                   sentiment = sentiment_data
# 将 image_data 转换为 Base64 编码的字符串
                image_data_base64 = base64.b64encode(row[4]).decode('utf-8')
                comment = {
                    'id': row[0],
                    'name': row[1],
                    'description': row[2],
                    'sentiment': sentiment,
                    'image_data': image_data_base64
                }
@app.route('/api/merged_images', methods=['GET'])
def get_merged_images():
    try:
        print("接收到 /api/merged_images 请求")
        query = 'SELECT id, image_name, description, emotions, image_data FROM merged_images'
        comments = fetch_comments(query)
        if comments is None:
            return jsonify({'error': '无法获取 merged_images 数据'}), 500

        print(f"查询到 {len(comments)} 条 merged_images 数据")
        return jsonify({'merged_images': comments})
    except Exception as e:
        print(f"错误: {e}")
        return jsonify({'error': str(e)}), 500

接口与数据可视化

fetch('http://localhost:5000/api/merged_images')
            .then(response => response.json())
            .then(data => {
                mergedImagesData = data.merged_images;

                // 初始化情绪计数,确保每张图片都被计数
                mergedImagesData.forEach(item => {
                    const emotionData = item.sentiment;

                    // 计算主导情绪
                    const dominantEmotion = Object.keys(emotionData).reduce((a, b) => emotionData[a] > emotionData[b] ? a : b);

                    if (emotionSummary.hasOwnProperty(dominantEmotion)) {
                        emotionSummary[dominantEmotion]++;
                    } else {
                        console.warn(`未识别的情感类别 "${dominantEmotion}" 出现在图片 ${item.image_name}`);
                    }

                    console.log(`图片 ${item.image_name} 的主导情感为: ${dominantEmotion}`);
                });

                renderTable(currentPage);

                // 绘制情绪摘要柱状图
                const ctx = document.getElementById('emotionChart').getContext('2d');

                const emotionChart = new Chart(ctx, {
                    type: 'bar',
                    data: {
                        labels: ['愤怒', '厌恶', '恐惧', '高兴', '悲伤', '惊讶', '中性'],
                        datasets: [{
                            label: '情感计数',
                            data: [
                                emotionSummary.angry,
                                emotionSummary.disgust,
                                emotionSummary.fear,
                                emotionSummary.happy,
                                emotionSummary.sad,
                                emotionSummary.surprise,
                                emotionSummary.neutral
                            ],
                            backgroundColor: [
                                '#c0392b',
                                '#a93226',
                                '#e67e22',
                                '#f1c40f',
                                '#3498db',
                                '#9b59b6',
                                '#95a5a6'
                            ],
                            hoverBackgroundColor: [
                                '#c0392b',
                                '#a93226',
                                '#e67e22',
                                '#f1c40f',
                                '#3498db',
                                '#9b59b6',
                                '#95a5a6'
                            ]
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                display: true,
                                position: 'bottom'
                            }
                            
                        },
                        scales: {
                            x: {
                                title: {
                                    display: true,
                                    text: '情感类型'
                                },
                                grid: {
                                    display: false // 删除 x 轴背景方格线
                                }
                            },
                            y: {
                                beginAtZero: true,
                                title: {
                                    display: true,
                                    text: '数量'
                                },
                                ticks: {
                                    stepSize: 1
                                },
                                grid: {
                                    display: false // 删除 y 轴背景方格线
                                }
                            }
                        }
                    }
                });
            })
            .catch(error => {
                console.error('获取数据时出现问题:', error);
            });
    });

    document.addEventListener('DOMContentLoaded', function () {
        const calendar = document.getElementById('calendar');
        const today = new Date();
        const year = today.getFullYear();
        const month = today.getMonth();
        const date = today.getDate();
        let isStamped = false; // Flag to track if the stamp has been applied

        function generateCalendar(year, month) {
            calendar.innerHTML = '';
            const firstDay = new Date(year, month, 1).getDay();
            const daysInMonth = new Date(year, month + 1, 0).getDate();

            for (let i = 0; i < firstDay; i++) {
                const emptyCell = document.createElement('div');
                calendar.appendChild(emptyCell);
            }

            for (let day = 1; day <= daysInMonth; day++) {
                const dayCell = document.createElement('div');
                dayCell.textContent = day;
                dayCell.classList.add('calendar-day');
                if (day === date) {
                    dayCell.classList.add('today');
                }
                calendar.appendChild(dayCell);
            }
        }

        generateCalendar(year, month);

        const clickableImage = document.querySelector('.clickable-image');
        clickableImage.addEventListener('click', function () {
            if (!isStamped) { // Check if the stamp has already been applied
                const todayCell = document.querySelector('.calendar-day.today');
                if (todayCell) {
                    const stamp = document.createElement('img');
                    stamp.src = 'img/fu.png';
                    stamp.classList.add('stamp');
                    todayCell.appendChild(stamp);

                    setTimeout(() => {
                        todayCell.classList.add('marked');
                        stamp.remove();

                        const smallImage = document.createElement('img');
                        smallImage.src = 'img/fu.png'; // Path to the small image
                        smallImage.classList.add('small-stamp');
                        todayCell.appendChild(smallImage);
                    }, 1000); // Adjust the timeout duration as needed

                    isStamped = true; // Set the flag to true after stamping
                }
            }
        });
    });
posted @   KaiInssy  阅读(84)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示