这个系统将模拟一个真实的舆情监控后台,具备以下核心功能:

html5制作网页舆情监控系统
(图片来源网络,侵删)
  1. 实时数据概览:显示监控的关键指标,如总提及量、情感倾向、热门话题等。
  2. 舆情信息列表:以表格形式展示最新的舆情信息,包括来源、标题、情感、时间等。
  3. 情感分析仪表盘:使用图表直观展示正面、负面、中性情感的比例。
  4. 热门关键词云:展示当前最受关注的关键词。
  5. 交互功能:包括筛选、搜索、查看详情等。

我们将使用纯前端技术来实现,并模拟动态数据更新,以展示其“实时”监控的能力。


第一步:项目结构

创建一个清晰的项目文件夹结构。

public-opinion-monitor/
├── index.html          # 主页面
├── css/
│   └── style.css       # 样式文件
├── js/
│   ├── data.js         # 模拟数据
│   └── main.js         # 主要逻辑
└── images/
    └── logo.png        # (可选) Logo 图片

第二步:HTML 结构 (index.html)

这是页面的骨架,使用 HTML5 语义化标签来组织内容,有助于 SEO 和可读性。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">舆情监控系统</title>
    <link rel="stylesheet" href="css/style.css">
    <!-- 引入字体图标库,Font Awesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
    <header class="main-header">
        <div class="container">
            <div class="logo">
                <i class="fas fa-chart-line"></i>
                <h1>舆情监控系统</h1>
            </div>
            <nav class="main-nav">
                <a href="#" class="active">仪表盘</a>
                <a href="#">报告</a>
                <a href="#">设置</a>
            </nav>
        </div>
    </header>
    <main class="main-content">
        <div class="container">
            <!-- 数据概览卡片 -->
            <section class="dashboard-overview">
                <div class="card">
                    <div class="card-icon">
                        <i class="fas fa-comments"></i>
                    </div>
                    <div class="card-content">
                        <h3>总提及量</h3>
                        <p class="card-value" id="total-mentions">0</p>
                        <p class="card-trend">较昨日 <span class="up">+12%</span></p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-icon negative">
                        <i class="fas fa-frown"></i>
                    </div>
                    <div class="card-content">
                        <h3>负面舆情</h3>
                        <p class="card-value" id="negative-count">0</p>
                        <p class="card-trend">需要关注</p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-icon positive">
                        <i class="fas fa-smile"></i>
                    </div>
                    <div class="card-content">
                        <h3>正面舆情</h3>
                        <p class="card-value" id="positive-count">0</p>
                        <p class="card-trend">保持良好</p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-icon neutral">
                        <i class="fas fa-meh"></i>
                    </div>
                    <div class="card-content">
                        <h3>中性舆情</h3>
                        <p class="card-value" id="neutral-count">0</p>
                        <p class="card-trend">正常范围</p>
                    </div>
                </div>
            </section>
            <!-- 图表和舆情列表区域 -->
            <div class="content-grid">
                <!-- 左侧:情感分析图表和关键词云 -->
                <aside class="sidebar">
                    <div class="chart-container">
                        <h2>情感分析</h2>
                        <canvas id="sentimentChart"></canvas>
                    </div>
                    <div class="keyword-cloud-container">
                        <h2>热门关键词</h2>
                        <div id="keyword-cloud">
                            <!-- 关键词将通过 JS 动态生成 -->
                        </div>
                    </div>
                </aside>
                <!-- 右侧:舆情信息列表 -->
                <section class="opinion-list">
                    <div class="list-header">
                        <h2>最新舆情动态</h2>
                        <div class="search-and-filter">
                            <input type="text" id="search-input" placeholder="搜索标题或来源...">
                            <select id="filter-sentiment">
                                <option value="all">全部情感</option>
                                <option value="positive">正面</option>
                                <option value="neutral">中性</option>
                                <option value="negative">负面</option>
                            </select>
                        </div>
                    </div>
                    <div class="table-wrapper">
                        <table id="opinion-table">
                            <thead>
                                <tr>
                                    <th>来源</th>
                                    <th>标题</th>
                                    <th>情感</th>
                                    <th>时间</th>
                                    <th>操作</th>
                                </tr>
                            </thead>
                            <tbody id="opinion-tbody">
                                <!-- 舆情数据将通过 JS 动态生成 -->
                            </tbody>
                        </table>
                    </div>
                </section>
            </div>
        </div>
    </main>
    <footer class="main-footer">
        <div class="container">
            <p>&copy; 2025 舆情监控系统. All rights reserved.</p>
        </div>
    </footer>
    <!-- 引入 Chart.js 库用于绘制图表 -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <!-- 引入我们自己的 JS 文件 -->
    <script src="js/data.js"></script>
    <script src="js/main.js"></script>
</body>
</html>

第三步:CSS 样式 (css/style.css)

样式让页面变得美观和专业,我们使用 Flexbox 和 Grid 布局来实现响应式设计。

html5制作网页舆情监控系统
(图片来源网络,侵删)
/* style.css */
:root {
    --primary-color: #3498db;
    --secondary-color: #2c3e50;
    --success-color: #2ecc71;
    --warning-color: #f39c12;
    --danger-color: #e74c3c;
    --light-bg: #ecf0f1;
    --white: #ffffff;
    --text-color: #333;
    --border-color: #ddd;
}
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: var(--light-bg);
    color: var(--text-color);
    line-height: 1.6;
}
.container {
    width: 90%;
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 20px;
}
/* Header */
.main-header {
    background-color: var(--secondary-color);
    color: var(--white);
    padding: 1rem 0;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.main-header .container {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.logo i { font-size: 1.8rem; margin-right: 10px; }
.logo h1 { font-size: 1.5rem; }
.main-nav a {
    color: var(--white);
    text-decoration: none;
    margin-left: 20px;
    padding: 5px 10px;
    border-radius: 4px;
    transition: background-color 0.3s;
}
.main-nav a.active, .main-nav a:hover { background-color: rgba(255,255,255,0.1); }
/* Main Content */
.main-content { padding: 2rem 0; }
/* Dashboard Overview */
.dashboard-overview {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 20px;
    margin-bottom: 2rem;
}
.card {
    background: var(--white);
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    display: flex;
    align-items: center;
}
.card-icon { font-size: 2.5rem; margin-right: 20px; }
.card-icon.positive { color: var(--success-color); }
.card-icon.negative { color: var(--danger-color); }
.card-icon.neutral { color: var(--warning-color); }
.card-content h3 { font-size: 0.9rem; color: #777; margin-bottom: 5px; }
.card-value { font-size: 2rem; font-weight: bold; margin-bottom: 5px; }
.card-trend { font-size: 0.8rem; }
.card-trend .up { color: var(--success-color); }
.card-trend .down { color: var(--danger-color); }
/* Content Grid */
.content-grid {
    display: grid;
    grid-template-columns: 350px 1fr;
    gap: 20px;
}
/* Sidebar */
.sidebar .chart-container, .sidebar .keyword-cloud-container {
    background: var(--white);
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    margin-bottom: 20px;
}
.sidebar h2 { margin-bottom: 15px; font-size: 1.2rem; }
#sentimentChart { max-height: 250px; }
/* Keyword Cloud */
#keyword-cloud {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
.keyword {
    background: var(--light-bg);
    padding: 5px 12px;
    border-radius: 20px;
    font-size: 0.9rem;
    transition: transform 0.2s, background-color 0.2s;
    cursor: pointer;
}
.keyword:hover {
    transform: scale(1.05);
    background-color: var(--primary-color);
    color: var(--white);
}
/* Opinion List */
.opinion-list {
    background: var(--white);
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.list-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
.search-and-filter {
    display: flex;
    gap: 10px;
}
.search-and-filter input, .search-and-filter select {
    padding: 8px 12px;
    border: 1px solid var(--border-color);
    border-radius: 4px;
    outline: none;
}
.search-and-filter input:focus { border-color: var(--primary-color); }
/* Table */
.table-wrapper {
    overflow-x: auto;
}
#opinion-table {
    width: 100%;
    border-collapse: collapse;
}
#opinion-table th, #opinion-table td {
    padding: 12px;
    text-align: left;
    border-bottom: 1px solid var(--border-color);
}
#opinion-table th {
    background-color: var(--light-bg);
    font-weight: 600;
}
#opinion-table tr:hover { background-color: #f9f9f9; }
.sentiment-badge {
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: bold;
}
.sentiment-badge.positive { background-color: rgba(46, 204, 113, 0.2); color: var(--success-color); }
.sentiment-badge.negative { background-color: rgba(231, 76, 60, 0.2); color: var(--danger-color); }
.sentiment-badge.neutral { background-color: rgba(243, 156, 18, 0.2); color: var(--warning-color); }
.btn-details {
    background: none;
    border: none;
    color: var(--primary-color);
    cursor: pointer;
    text-decoration: underline;
    font-size: 0.9rem;
}
.btn-details:hover { opacity: 0.8; }
/* Footer */
.main-footer {
    background-color: var(--secondary-color);
    color: var(--white);
    text-align: center;
    padding: 1rem 0;
    margin-top: 2rem;
}

第四步:模拟数据 (js/data.js)

为了让系统可以运行,我们创建一个模拟数据文件,这些数据将代表从后端 API 获取的舆情信息。

// js/data.js
// 模拟舆情数据
const mockOpinionData = [
    { id: 1, source: '新浪微博', title: '某品牌新品发布会,网友反响热烈!', sentiment: 'positive', time: '2025-10-27 10:30:15', url: '#' },
    { id: 2, source: '知乎', title: '如何评价某手机这次的电池续航问题?', sentiment: 'negative', time: '2025-10-27 09:45:22', url: '#' },
    { id: 3, source: '微信公众号', title: '行业分析:未来十年的发展趋势预测', sentiment: 'neutral', time: '2025-10-27 08:20:11', url: '#' },
    { id: 4, source: '抖音', title: '这个产品太好用了,无限回购!', sentiment: 'positive', time: '2025-10-27 11:05:33', url: '#' },
    { id: 5, source: '新闻头条', title: '公司因管理不善被曝出丑闻,股价大跌', sentiment: 'negative', time: '2025-10-27 07:55:44', url: '#' },
    { id: 6, source: 'B站', title: '深度测评:这款耳机到底值不值得买?', sentiment: 'neutral', time: '2025-10-27 12:15:00', url: '#' },
    { id: 7, source: '小红书', title: 'OMG!这个口红颜色绝了,黄皮友好!', sentiment: 'positive', time: '2025-10-27 13:00:05', url: '#' },
    { id: 8, source: '天涯论坛', title: '老员工爆料:公司内部问题重重', sentiment: 'negative', time: '2025-10-27 06:30:10', url: '#' }
];
// 模拟关键词数据
const mockKeywords = [
    { text: '新品发布', weight: 10 },
    { text: '电池续航', weight: 9 },
    { text: '行业分析', weight: 7 },
    { text: '管理不善', weight: 8 },
    { text: '深度测评', weight: 6 },
    { text: '黄皮友好', weight: 5 },
    { text: '内部爆料', weight: 9 },
    { text: '股价大跌', weight: 7 },
    { text: '无限回购', weight: 6 },
    { text: '发展趋势', weight: 5 }
];
// 模拟实时数据更新函数
function addNewOpinion() {
    const sources = ['新浪微博', '知乎', '微信公众号', '抖音', '新闻头条', 'B站', '小红书', '天涯论坛'];
    const titles = [
        '突发新闻!某公司宣布重大战略合作',
        '用户体验报告:这个APP真的好用吗?',
        '专家解读:新政策将如何影响市场',
        '又一个网红产品火了,你买了吗?',
        '公司发布财报,业绩超出预期',
        'Vlog:我的日常好物分享',
        '避雷指南:这些产品千万别买!',
        '员工福利提升,大家都很开心'
    ];
    const sentiments = ['positive', 'negative', 'neutral'];
    const newOpinion = {
        id: mockOpinionData.length + 1,
        source: sources[Math.floor(Math.random() * sources.length)],
        title: titles[Math.floor(Math.random() * titles.length)],
        sentiment: sentiments[Math.floor(Math.random() * sentiments.length)],
        time: new Date().toLocaleString('zh-CN'),
        url: '#'
    };
    // 添加到数据开头
    mockOpinionData.unshift(newOpinion);
    // 保持数据量不超过20条
    if (mockOpinionData.length > 20) {
        mockOpinionData.pop();
    }
    // 重新渲染列表和更新统计
    renderOpinionTable(mockOpinionData);
    updateDashboardStats();
}

第五步:核心 JavaScript 逻辑 (js/main.js)

这是系统的大脑,负责数据渲染、事件处理和图表绘制。

// js/main.js
document.addEventListener('DOMContentLoaded', () => {
    // --- 初始化 ---
    renderOpinionTable(mockOpinionData);
    renderKeywordCloud(mockKeywords);
    updateDashboardStats();
    initSentimentChart();
    // --- 事件监听器 ---
    const searchInput = document.getElementById('search-input');
    const filterSentiment = document.getElementById('filter-sentiment');
    searchInput.addEventListener('input', filterOpinions);
    filterSentiment.addEventListener('change', filterOpinions);
    // --- 模拟实时更新 ---
    // 每5秒添加一条新数据
    setInterval(addNewOpinion, 5000);
    // 每10秒更新一次关键词(模拟)
    setInterval(() => {
        // 随机更新一个关键词的权重
        const randomIndex = Math.floor(Math.random() * mockKeywords.length);
        mockKeywords[randomIndex].weight += Math.floor(Math.random() * 3) - 1;
        renderKeywordCloud(mockKeywords);
    }, 10000);
    // --- 函数定义 ---
    /**
     * 渲染舆情列表表格
     * @param {Array} data - 要渲染的数据
     */
    function renderOpinionTable(data) {
        const tbody = document.getElementById('opinion-tbody');
        tbody.innerHTML = ''; // 清空现有内容
        data.forEach(item => {
            const row = document.createElement('tr');
            row.innerHTML = `
                <td>${item.source}</td>
                <td>${item.title}</td>
                <td><span class="sentiment-badge ${item.sentiment}">${getSentimentText(item.sentiment)}</span></td>
                <td>${item.time}</td>
                <td><a href="${item.url}" class="btn-details">查看详情</a></td>
            `;
            tbody.appendChild(row);
        });
    }
    /**
     * 渲染关键词云
     * @param {Array} keywords - 关键词数组
     */
    function renderKeywordCloud(keywords) {
        const cloudContainer = document.getElementById('keyword-cloud');
        cloudContainer.innerHTML = '';
        // 找出最大权重用于缩放
        const maxWeight = Math.max(...keywords.map(k => k.weight));
        keywords.forEach(keyword => {
            const span = document.createElement('span');
            span.className = 'keyword';
            span.textContent = keyword.text;
            // 根据权重设置字体大小
            const fontSize = 0.8 + (keyword.weight / maxWeight) * 1.2; // 0.8rem 到 2.0rem
            span.style.fontSize = `${fontSize}rem`;
            cloudContainer.appendChild(span);
        });
    }
    /**
     * 更新仪表盘统计数据
     */
    function updateDashboardStats() {
        const stats = mockOpinionData.reduce((acc, item) => {
            acc.total++;
            acc[item.sentiment]++;
            return acc;
        }, { total: 0, positive: 0, negative: 0, neutral: 0 });
        document.getElementById('total-mentions').textContent = stats.total;
        document.getElementById('positive-count').textContent = stats.positive;
        document.getElementById('negative-count').textContent = stats.negative;
        document.getElementById('neutral-count').textContent = stats.neutral;
        // 更新图表
        if (window.sentimentChart) {
            window.sentimentChart.data.datasets[0].data = [stats.positive, stats.neutral, stats.negative];
            window.sentimentChart.update();
        }
    }
    /**
     * 初始化情感分析图表
     */
    function initSentimentChart() {
        const ctx = document.getElementById('sentimentChart').getContext('2d');
        const stats = mockOpinionData.reduce((acc, item) => {
            acc[item.sentiment]++;
            return acc;
        }, { positive: 0, neutral: 0, negative: 0 });
        window.sentimentChart = new Chart(ctx, {
            type: 'doughnut',
            data: {
                labels: ['正面', '中性', '负面'],
                datasets: [{
                    data: [stats.positive, stats.neutral, stats.negative],
                    backgroundColor: [
                        'rgba(46, 204, 113, 0.8)', // 绿色 - 正面
                        'rgba(243, 156, 18, 0.8)', // 橙色 - 中性
                        'rgba(231, 76, 60, 0.8)'  // 红色 - 负面
                    ],
                    borderColor: [
                        'rgba(46, 204, 113, 1)',
                        'rgba(243, 156, 18, 1)',
                        'rgba(231, 76, 60, 1)'
                    ],
                    borderWidth: 1
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        position: 'bottom',
                    }
                }
            }
        });
    }
    /**
     * 根据情感类型返回对应的中文文本
     * @param {string} sentiment - 情感类型
     * @returns {string}
     */
    function getSentimentText(sentiment) {
        const sentimentMap = {
            'positive': '正面',
            'neutral': '中性',
            'negative': '负面'
        };
        return sentimentMap[sentiment] || sentiment;
    }
    /**
     * 筛选舆情列表
     */
    function filterOpinions() {
        const searchTerm = document.getElementById('search-input').value.toLowerCase();
        const sentimentFilter = document.getElementById('filter-sentiment').value;
        const filteredData = mockOpinionData.filter(item => {
            const matchesSearch = item.title.toLowerCase().includes(searchTerm) || item.source.toLowerCase().includes(searchTerm);
            const matchesSentiment = sentimentFilter === 'all' || item.sentiment === sentimentFilter;
            return matchesSearch && matchesSentiment;
        });
        renderOpinionTable(filteredData);
    }
});

如何运行

  1. 将以上四个文件(index.html, style.css, data.js, main.js)按照目录结构放置。
  2. 直接用浏览器打开 index.html 文件即可看到效果。

功能说明

  • 实时模拟:系统会每5秒自动添加一条新的模拟舆情,并更新列表和统计数据,模拟实时监控的效果。
  • 数据可视化:使用 Chart.js 库绘制了情感分析的饼图,直观展示舆情分布。
  • 交互式筛选:可以通过输入关键词和选择情感类型来筛选舆情列表。
  • 响应式设计:使用 CSS Grid 和 Flexbox,页面在不同屏幕尺寸下都能良好显示。
  • 语义化 HTML5:使用了 <header>, <main>, <section>, <aside>, <footer> 等标签,使代码结构清晰。

未来可以扩展的方向

  • 后端集成:将 data.js 中的模拟数据替换为通过 fetchaxios 从真实的后端 API 获取数据。
  • 更复杂的图表:增加时间序列图表,展示舆情量随时间的变化趋势。
  • 详情页:为“查看详情”按钮添加功能,点击后可以跳转到或弹出一个展示该舆情详细信息的页面。
  • 数据导出:增加将当前筛选后的数据导出为 Excel 或 CSV 文件的功能。
  • 高级筛选:增加按时间范围、来源网站等更复杂的筛选条件。
  • 告警系统:当负面舆情数量超过某个阈值时,页面可以高亮显示或发送通知。

这个项目是一个功能完整、设计现代的舆情监控系统前端原型,很好地展示了 HTML5、CSS3 和 JavaScript 的综合运用能力。

html5制作网页舆情监控系统
(图片来源网络,侵删)