数据采集融合与应用综合实践
福州大学多模态网络舆情分析与可视化系统
序号 | 信息类别 | 内容描述 |
---|---|---|
1 | 这个项目属于哪个课程 | 数据采集与融合综合实践 |
2 | 组名、项目简介 | 组名:福小兵,项目需求:实时舆情监控系统,项目目标:为福州大学提供舆情监控与决策辅助工具,技术路线:使用 Flask 后端、Memfire(PostgreSQL)数据库和 Vue 前端技术栈,建立从数据采集到情感分析再到可视化的完整系统 |
3 | 团队成员学号 | 102202141黄昕怡, 102202112刘莹,102202145谢含, 102202101马鑫,102202106王强,102202126陈家凯,102202153来再提·叶鲁别克,102202124 阿依娜孜·赛日克 |
4 | 这个项目的目标 | 设计并实现一个多源异构数据采集系统,通过情感分析和大数据技术总结和展示舆情,增强学校管理者对校园舆情的理解和控制力度。 |
5 | 其他参考文献 | [1] 张玲, 王磊, & 李铭. (2021). 面向社交媒体情感分析的多模态数据融合方法研究. 图书情报工作, 65(8), 21-28. [2] 王泽, 刘辉, & 张宇翔. (2020). 基于深度学习的多模态数据融合与情感分析. 计算机研究与发展, 57(7), 1430-1440. [3] 吴鹏, 王敏, & 刘洪. (2023). 面向微博情感分析的多模态数据联合建模方法. 信息与控制, 52(2), 126-134. [4] 赵妍妍, 秦兵, 刘挺.文本情感分析[J].软件学报, 2010(8):15.DOI:10.3724/SP.J.1001.2010.03832. |
项目github代码
代码仓库
码云
一、项目概述
随着互联网和社交媒体的飞速发展,高校舆情已成为影响学校形象与声誉的重要因素,同时无论作为本校学生或是校外人员都对福州大学相关舆情格外关注,这不仅直接影响学校声誉,还间接影响未来生源与发展,所以作为福州大学学生,我们组决定制作一款聚焦在微博、贴吧、知乎等平台的多模态网络舆情数据,以Flask 后端、Memfire(PostgreSQL) 数据库和 Vue 前端 为主要技术栈,搭建一个从数据采集到情感分析再到可视化展示的完整系统,为学校舆情监控与管理提供支持和决策辅助,助力学校有更美好的明天和未来。
项目主要目标:
多源异构数据采集:整合文本、图片等多模态信息,建立统一的数据存储和管理方案。
情感分析与大模型总结:基于零样本分类模型进行细粒度情感识别,并调用大语言模型生成舆情报告和情感分析走向。
可视化与交互:通过前端页面实现搜索、分析结果图表展示、舆情总结文本等交互功能。
二、系统总体结构
1. 数据采集层
- 通过爬虫或官方 API,从微博、贴吧、知乎等平台获取与福州大学相关的多模态内容(文本、图片、视频链接等)。
- 数据存储到 Memfire 提供的云端 PostgreSQL 数据库中,便于后续查询与分析。
2. 后端层(Flask)
- 提供 RESTful API,对外暴露功能接口包括:
- 关键词搜索:在数据库中检索相关内容;
- 情感分析:基于深度学习模型,对文本做多类别情感识别;
- 舆情整合:关键词提取、高频话题检测、情感分析统计,AI 总结。
- 整合大语言模型接口,用于对检索和情感分析结果进行二次生成与深入总结。
3. 前端层(Vue + HTML/CSS)
- 前端一包括用户自爬取数据和报告式分析使用 Vue 搭建前端页面,包含搜索输入框、情感分析可视化、AI 总结输出等界面。
- 前端二包括用户聊天式舆论分析和全数据库整合 整体数据分析整合表格分页、柱状/饼图、词云翻页、音乐播放器、日历打卡彩蛋、AI 聊天窗口等交互;
- 对接后端 Flask API,利用 Axios 发起请求,将搜索与分析结果进行可视化呈现(Chart.js 等图表库)。
三、项目功能展示
1.数据采集和整体数据展示
2.自动爬取存取数据库与自动检索
3.用户情感分析
4. 舆情总结(大语言模型)
四、项目亮点
- 多模态融合:多模块情感分析,包括文本、图片,以及对多个平台的数据进行爬取和分析,通过扩大获取数据量进一步对福大学生情感进行准确分析
- 大模型赋能:加入大语言模型,对上传与爬取的数据进行只能分析反馈,让用户更好了解相关数据
- 数据可视化:采用图表的方式对情感数据进行可视化、情感偏向的数量统计、自动化报告帮助用户及时掌握网络舆论走向
- 用户体验感:页面整洁大方,同时加入背景音乐、日历打卡、跳转翻页和自动化报告,提升用户体验感
五、未来展望
将数据库中的数据量进一步丰富,例如可以爬取更多平台的相关数据(例如小红书),或增加平台内的数据爬取(例如二级评论),以增加数据量的途径进一步提高准确率,同时可分析不同平台上用户的情感偏向。给可视化界面增加更多功能,提升用户体验感(例如增加多首歌曲)、在数据行中附上对应链接,使用户可以随时可以跳转到该帖子、增加对敏感词汇的自动检测,方便用户进一步加深的数据的了解。
六、个人贡献
主要负责数据展示的前端设计与美化,同时包括连接后端相应的数据库、相应数据的数据库表设计并在前端进行展示与统计
编写的相关代码
贴吧评论界面
<script>
const itemsPerPage = 10; // 每页显示10条
let currentPage = 1;
let sentimentData = [];
// 加载并渲染评论情感数据
async function loadComments() {
try {
console.log("开始加载评论分析数据");
// 从后端 API 获取数据
const response = await fetch('http://localhost:5000/api/comments2');
if (!response.ok) {
throw new Error(`HTTP 错误! 状态: ${response.status}`);
}
const result = await response.json();
console.log("获取到的评论分析结果:", result);
sentimentData = result.comments;
renderTable(currentPage);
renderSentimentChart(sentimentData);
} catch (error) {
console.error('无法加载评论分析数据:', error);
}
}
// 渲染表格数据
function renderTable(page) {
const tableBody = document.getElementById('commentsTableBody');
tableBody.innerHTML = ''; // 清空表格
const startIndex = (page - 1) * itemsPerPage;
const endIndex = Math.min(startIndex + itemsPerPage, sentimentData.length);
const pageData = sentimentData.slice(startIndex, endIndex);
pageData.forEach((comment, index) => {
const tr = document.createElement('tr');
// 添加序号单元格
const tdIndex = document.createElement('td');
tdIndex.textContent = startIndex + index + 1;
tr.appendChild(tdIndex);
// 添加文本单元格
const tdText = document.createElement('td');
tdText.textContent = comment.text;
tr.appendChild(tdText);
// 添加情感单元格
const tdSentiment = document.createElement('td');
tdSentiment.textContent = comment.sentiment;
tr.appendChild(tdSentiment);
// 添加分数单元格
const tdScore = document.createElement('td');
tdScore.textContent = comment.score;
tr.appendChild(tdScore);
tableBody.appendChild(tr);
});
updatePaginationInfo(page, sentimentData.length);
}
// 更新分页信息
function updatePaginationInfo(page, totalItems) {
const totalPages = Math.ceil(totalItems / itemsPerPage);
document.getElementById('pageInfo').textContent = `第 ${page} 页,共 ${totalPages} 页`;
document.getElementById('prevPage').disabled = page === 1;
document.getElementById('nextPage').disabled = page === totalPages;
}
// 分页按钮事件
document.getElementById('prevPage').onclick = function () {
if (currentPage > 1) {
currentPage--;
renderTable(currentPage);
}
};
document.getElementById('nextPage').onclick = function () {
const totalPages = Math.ceil(sentimentData.length / itemsPerPage);
if (currentPage < totalPages) {
currentPage++;
renderTable(currentPage);
}
};
document.getElementById('jumpPageButton').onclick = function () {
const jumpPage = parseInt(document.getElementById('jumpToPage').value);
const totalPages = Math.ceil(sentimentData.length / itemsPerPage);
if (!isNaN(jumpPage) && jumpPage >= 1 && jumpPage <= totalPages) {
currentPage = jumpPage;
renderTable(currentPage);
} else {
alert('请输入有效的页码');
}
};
// 渲染情感分布图表
function renderSentimentChart(comments) {
const sentimentCounts = {};
comments.forEach(comment => {
if (sentimentCounts.hasOwnProperty(comment.sentiment)) {
sentimentCounts[comment.sentiment]++;
} else {
sentimentCounts[comment.sentiment] = 1;
}
});
const ctx = document.getElementById('emotionChart').getContext('2d');
// 销毁旧图表(如果存在)
if (window.emotionChartInstance) {
window.emotionChartInstance.destroy();
}
window.emotionChartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: Object.keys(sentimentCounts),
datasets: [{
label: '情感总结',
data: Object.values(sentimentCounts),
backgroundColor: [
'#c0392b',
'#3498db', // 悲伤
'#9b59b6', // 惊讶
'#95a5a6'
], // 与 index.html 一致的颜色
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: true
},
datalabels: {
anchor: 'end',
align: 'top',
offset: -10,
color: '#333',
font: {
size: 14,
weight: 'bold'
},
formatter: function(value) {
return value;
}
}
},
scales: {
x: {
title: {
display: true,
text: '情感类型'
},
grid: {
display: false // 删除 x 轴背景方格线
}
},
y: {
beginAtZero: true,
title: {
display: true,
text: '数量'
},
ticks: {
stepSize: 1
},
grid: {
display: false // 删除 y 轴背景方格线
}
}
}
},
plugins: [ChartDataLabels]
});
}
// 页面加载完成后调用函数
document.addEventListener('DOMContentLoaded', loadComments);
微博评论界面
document.addEventListener('DOMContentLoaded', function () {
let currentPage = 1;
const itemsPerPage = 10;
let data = [];
fetch('http://127.0.0.1:5000/api/comments')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP 错误! 状态: ${response.status}`);
}
return response.json();
})
.then(jsonData => {
if (jsonData.comments) {
data = jsonData.comments;
renderTable(currentPage);
renderBarChart(); // 确保数据加载后绘制柱形图
} else {
console.error('No comments data found.');
}
})
.catch(error => {
console.error('Error fetching data:', error);
});
function renderTable(page) {
const tableBody = document.getElementById('commentsTableBody');
tableBody.innerHTML = '';
const start = (page - 1) * itemsPerPage;
const end = start + itemsPerPage;
const pageData = data.slice(start, end);
console.log(`Rendering page ${page} data:`, pageData); // 调试用
pageData.forEach(comment => {
const row = document.createElement('tr');
// 显示 id
const idCell = document.createElement('td');
idCell.textContent = comment.id;
row.appendChild(idCell);
// 显示昵称
const nicknameCell = document.createElement('td');
nicknameCell.textContent = comment.nickname;
row.appendChild(nicknameCell);
// 显示内容
const contentCell = document.createElement('td');
contentCell.textContent = comment.content;
row.appendChild(contentCell);
// 显示情感分析为饼图
const sentimentCell = document.createElement('td');
const canvas = document.createElement('canvas');
canvas.width = 250; // 设置饼图宽度
canvas.height = 250; // 设置饼图高度
sentimentCell.appendChild(canvas);
row.appendChild(sentimentCell);
tableBody.appendChild(row);
// 准备饼图数据
const emotions = Object.keys(comment.sentiment);
const values = Object.values(comment.sentiment);
// 生成随机颜色
const backgroundColors = emotions.map(() => `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.6)`);
// 创建饼图
new Chart(canvas.getContext('2d'), {
type: 'pie',
data: {
labels: emotions,
datasets: [{
data: values,
backgroundColor: backgroundColors,
borderColor: 'rgba(255, 255, 255, 1)',
borderWidth: 1
}]
},
options: {
responsive: false,
plugins: {
legend: {
position: 'bottom',
labels: {
font: {
size: 10
}
}
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
return `${label}: ${value}`;
}
}
}
}
}
});
});
// 计算总页数
const totalPages = Math.ceil(data.length / itemsPerPage);
document.getElementById('pageInfo').textContent = `第 ${page} 页,共 ${totalPages} 页`;
// 更新分页按钮状态
document.getElementById('prevPage').disabled = page === 1;
document.getElementById('nextPage').disabled = page === totalPages;
}
function renderBarChart() {
const ctx = document.getElementById('barChart').getContext('2d');
const emotionLabels = ['愤怒', '厌恶', '恐惧', '喜悦', '悲伤', '惊讶', '中性'];
const emotionTotals = {
'愤怒': 0,
'厌恶': 0,
'恐惧': 0,
'喜悦': 0,
'悲伤': 0,
'惊讶': 0,
'中性': 0
};
data.forEach(item => {
let maxEmotion = '中性';
let maxValue = 0;
for (let emotion in item.sentiment) {
if (item.sentiment[emotion] > maxValue) {
maxValue = item.sentiment[emotion];
maxEmotion = emotion;
}
}
emotionTotals[maxEmotion]++;
});
const emotionValues = Object.values(emotionTotals);
new Chart(ctx, {
type: 'bar',
data: {
labels: emotionLabels,
datasets: [{
label: '情绪总结',
data: emotionValues,
backgroundColor: [
'#c0392b',
'#a93226',
'#e67e22',
'#f1c40f',
'#3498db',
'#9b59b6',
'#95a5a6'
],
borderColor: [
'#c0392b',
'#a93226',
'#e67e22',
'#f1c40f',
'#3498db',
'#9b59b6',
'#95a5a6'
],
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
ticks: {
precision: 0,
font: {
size: 14
},
callback: function(value) {
if (Number.isInteger(value)) {
return value;
}
}
},
grid: {
display: false
}
},
x: {
ticks: {
font: {
size: 14
}
},
grid: {
display: false
}
}
},
plugins: {
datalabels: {
anchor: 'end',
align: 'end',
offset: -10,
color: '#333',
font: {
size: 14,
weight: 'bold'
},
formatter: function(value) {
return Math.round(value);
}
}
}
},
plugins: [ChartDataLabels]
});
}
document.getElementById('prevPage').addEventListener('click', function () {
if (currentPage > 1) {
currentPage--;
renderTable(currentPage);
}
});
document.getElementById('nextPage').addEventListener('click', function () {
if ((currentPage * itemsPerPage) < data.length) {
currentPage++;
renderTable(currentPage);
}
});
document.getElementById('jumpPageButton').addEventListener('click', function () {
const jumpToPage = parseInt(document.getElementById('jumpToPage').value);
if (jumpToPage > 0 && jumpToPage <= Math.ceil(data.length / itemsPerPage)) {
currentPage = jumpToPage;
renderTable(currentPage);
}
});
});
// 图表翻页功能
document.getElementById('prevChart').addEventListener('click', function () {
if (currentChart === 'bar') {
currentChart = 'wordCloud';
} else {
currentChart = 'bar';
}
toggleChart();
});
document.getElementById('nextChart').addEventListener('click', function () {
if (currentChart === 'wordCloud') {
currentChart = 'bar';
} else {
currentChart = 'wordCloud';
}
toggleChart();
});
function toggleChart() {
if (currentChart === 'bar') {
document.getElementById('barChart').style.display = 'block';
document.getElementById('wordCloud').style.display = 'none';
} else {
document.getElementById('barChart').style.display = 'none';
document.getElementById('wordCloud').style.display = 'block';
}
}
图片分析界面
document.addEventListener('DOMContentLoaded', function () {
let currentPage = 1;
const itemsPerPage = 10;
let mergedImagesData = [];
let emotionSummary = {
angry: 0,
disgust: 0,
fear: 0,
happy: 0,
sad: 0,
surprise: 0,
neutral: 0
};
let currentChart = 'bar'; // 当前显示的图表类型
function renderTable(page) {
const tableBody = document.getElementById('commentsTableBody');
tableBody.innerHTML = '';
const start = (page - 1) * itemsPerPage;
const end = start + itemsPerPage;
const pageData = mergedImagesData.slice(start, end);
pageData.forEach((item, index) => {
const row = document.createElement('tr');
const imageSrc = `data:image/jpeg;base64,${item.image_data}`;
row.innerHTML = `
<td>${start + index + 1}</td>
<td><img src="${imageSrc}" alt="评论图片"></td>
<td>${item.description}</td>
<td><div class="emotion-chart"><canvas id="chart-${start + index}" class="emotion-chart"></canvas></div></td>
`;
tableBody.appendChild(row);
const ctx = document.getElementById(`chart-${start + index}`).getContext('2d');
const emotionData = item.sentiment;
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');
});
});
const totalPages = Math.ceil(mergedImagesData.length / itemsPerPage);
document.getElementById('pageInfo').textContent = `第 ${page} 页,共 ${totalPages} 页`;
document.getElementById('prevPage').disabled = currentPage === 1;
document.getElementById('nextPage').disabled = currentPage === totalPages;
}
// 循环翻页
document.getElementById('prevPage').onclick = function () {
if (currentPage > 1) {
currentPage--;
renderTable(currentPage);
}
};
document.getElementById('nextPage').onclick = function () {
if (currentPage < Math.ceil(mergedImagesData.length / itemsPerPage)) {
currentPage++;
renderTable(currentPage);
}
};
document.getElementById('jumpPageButton').onclick = function () {
const jumpToPage = parseInt(document.getElementById('jumpToPage').value);
if (jumpToPage >= 1 && jumpToPage <= Math.ceil(mergedImagesData.length / itemsPerPage)) {
currentPage = jumpToPage;
renderTable(currentPage);
}
};
// 图表翻页功能
document.getElementById('prevChart').addEventListener('click', function () {
if (currentChart === 'bar') {
currentChart = 'wordCloud';
} else {
currentChart = 'bar';
}
toggleChart();
});
document.getElementById('nextChart').addEventListener('click', function () {
if (currentChart === 'wordCloud') {
currentChart = 'bar';
} else {
currentChart = 'wordCloud';
}
toggleChart();
});
function toggleChart() {
if (currentChart === 'bar') {
document.getElementById('emotionChart').style.display = 'block';
document.getElementById('wordCloud').style.display = 'none';
} else {
document.getElementById('emotionChart').style.display = 'none';
document.getElementById('wordCloud').style.display = 'block';
}
}
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);
});
});
爬取数据上传到数据库
import json
import psycopg2
# 读取 JSON 文件
with open('analyzed_demoone_filtered.json', 'r', encoding='utf-8') as f:
data = json.load(f)
comments = data['comments']
# 连接到数据库
conn = psycopg2.connect(
host='101.132.80.183', # 替换为您的主机地址
port='5433', # 替换为您的端口号
database='dbf72a0c3d7d054ef39b98488f2995d159zz', # 替换为您的数据库名
user='zzsthere', # 替换为您的用户名
password='20020925Aa' # 替换为您的密码
)
cursor = conn.cursor()
# 插入数据
insert_sql = """
INSERT INTO analyzed_demo_one (nickname, content, sentiment)
VALUES (%s, %s, %s)
"""
for comment in comments:
nickname = comment['nickname']
content = comment['content']
sentiment = json.dumps(comment['sentiment'], ensure_ascii=False) # 转换为 JSON 字符串
cursor.execute(insert_sql, (nickname, content, sentiment))
# 提交事务并关闭连接
conn.commit()
cursor.close()
conn.close()
print("数据插入完成。")
搭建与数据库的联系
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
def fetch_comments(query, params=None):
try:
conn = get_db_connection()
if conn is None:
return None
cursor = conn.cursor()
cursor.execute(query, params)
rows = cursor.fetchall()
cursor.close()
conn.close()
comments = []
for row in rows:
if 'comment_analyse' in query:
# 处理 comment_analyse 表数据
score_data = row[3]
try:
score = json.loads(score_data)
except (TypeError, json.JSONDecodeError):
score = score_data
comment = {
'id': row[0],
'text': row[1],
'sentiment': row[2],
'score': score
}
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
}
else:
# 处理 analyzed_demo_one 表数据
sentiment_data = row[3]
try:
sentiment = json.loads(sentiment_data)
except (TypeError, json.JSONDecodeError):
sentiment = sentiment_data
comment = {
'id': row[0],
'nickname': row[1],
'content': row[2],
'sentiment': sentiment
}
comments.append(comment)
return comments
except Exception as e:
print(f"查询错误: {e}")
return None
@app.route('/api/comments', methods=['GET'])
def get_comments():
try:
print("接收到 /api/comments 请求")
query = 'SELECT id, nickname, content, sentiment FROM analyzed_demo_one'
comments = fetch_comments(query)
if comments is None:
return jsonify({'error': '无法获取评论数据'}), 500
print(f"查询到 {len(comments)} 条评论")
return jsonify({'comments': comments})
except Exception as e:
print(f"错误: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/comments2', methods=['GET'])
def get_comments2():
try:
print("接收到 /api/comments2 请求")
query = 'SELECT id, text, sentiment, score FROM comment_analyse'
comments = fetch_comments(query)
if comments is None:
return jsonify({'error': '无法获取评论分析数据'}), 500
print(f"查询到 {len(comments)} 条评论分析数据")
return jsonify({'comments': comments})
except Exception as e:
print(f"错误: {e}")
return jsonify({'error': str(e)}), 500