基于Flask+Echarts+爬虫的疫情监控系统

'# 基于Flask+Echarts+爬虫的疫情监控系统

一、背景与问题

随着全球疫情形势的动态变化,实时获取和可视化疫情数据成为公共卫生管理的重要环节。传统疫情监控系统面临三大挑战:

  1. 数据获取困难:政府网站数据更新滞后,手动收集效率低下
  2. 数据可视化需求:需要多维度图表展示(折线图/柱状图/热力图)
  3. 系统可维护性:需要支持定时更新、数据持久化和多终端访问

传统解决方案往往采用静态网页+Excel表格,存在数据延迟、更新繁琐等问题。本文提出的解决方案通过Flask构建后端服务,ECharts实现动态可视化,结合爬虫技术实现数据自动更新,形成完整的疫情监控闭环系统。

二、基本原理

1. 技术架构

系统采用分层架构:

[用户终端] → [Flask API] → [数据存储] → [爬虫服务]
          ↑                         ↓
          [ECharts前端]            [数据源]
  • 爬虫层:使用requests+BeautifulSoup解析目标网站,提取疫情数据
  • 后端层:Flask提供RESTful API,支持数据查询和图表配置
  • 前端层:ECharts动态渲染图表,支持实时数据更新
  • 存储层:SQLite存储历史数据,支持快速查询

2. 数据处理流程

  1. 爬虫定时采集最新数据
  2. 数据清洗:去除异常值、格式标准化
  3. 存储到数据库
  4. 前端请求数据时,Flask返回结构化数据
  5. ECharts根据配置生成动态图表

三、环境准备

# 安装依赖
pip install flask requests beautifulsoup4 echartsc  sqlite3

项目结构:

covid_monitor/
├── app.py              # Flask主程序
├── data/               # 存储数据文件
│   └── history.db      # SQLite数据库
├── crawlers/           # 爬虫模块
│   └── crawler.py      # 爬虫核心代码
├── templates/          # 前端模板
│   └── index.html      # ECharts页面
└── static/             # 静态资源
    └── style.css       # 样式文件

四、核心实现

1. 爬虫模块实现(crawler.py)

import requests
from bs4 import BeautifulSoup
import sqlite3
import time
import random

# 爬虫配置
HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4443.116 Safari/537.36'
}

def fetch_data():
    """爬取疫情数据"""
    url = 'https://example-covid-data.com'
    try:
        response = requests.get(url, headers=HEADERS, timeout=10)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 假设数据在表格中
        table = soup.find('table', class_='covid-data')
        rows = table.find_all('tr')
        
        data = []
        for row in rows[1:]:  # 跳过表头
            cols = row.find_all(['td', 'th'])
            if len(cols) == 0:
                continue
            region = cols[0].text.strip()
            confirmed = int(cols[1].text.replace(',', '').strip())
            cured = int(cols[2].text.replace(',', '').strip())
            dead = int(cols[3].text.replace(',', '').strip())
            data.append({
                'region': region,
                'confirmed': confirmed,
                'cured': cured,
                'dead': dead,
                'timestamp': int(time.time())
            })
        
        return data
    except Exception as e:
        print(f"爬虫错误: {str(e)}")
        return []

def save_to_db(data):
    """保存数据到SQLite"""
    conn = sqlite3.connect('history.db')
    cursor = conn.cursor()
    
    # 创建表(如果不存在)
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS stats (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            region TEXT,
            confirmed INTEGER,
            cured INTEGER,
            dead INTEGER,
            timestamp INTEGER
        )
    ''')
    
    # 插入新数据
    cursor.executemany('''
        INSERT INTO stats (region, confirmed, cured, dead, timestamp)
        VALUES (?, ?, ?, ?, ?)
    ''', data)
    conn.commit()
    conn.close()

def run_crawler():
    """定时爬虫函数"""
    while True:
        print("开始爬取数据...")
        data = fetch_data()
        if data:
            save_to_db(data)
            print(f"成功保存{len(data)}条数据")
        else:
            print("未获取到新数据")
        time.sleep(3600)  # 每小时运行一次

关键点说明:

  • 使用随机User-Agent防止反爬
  • 数据清洗处理千分位分隔符
  • 使用SQLite保证轻量级存储
  • 爬虫服务采用轮询机制

2. Flask接口实现(app.py)

from flask import Flask, jsonify, render_template
import sqlite3
import json

app = Flask(__name__)

# 数据库配置
DB_PATH = 'history.db'

def get_all_data():
    """获取所有历史数据"""
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM stats ORDER BY timestamp DESC")
    rows = cursor.fetchall()
    conn.close()
    
    # 转换为JSON格式
    return [{
        'id': row[0],
        'region': row[1],
        'confirmed': row[2],
        'cured': row[3],
        'dead': row[4],
        'timestamp': row[5]
    } for row in rows]

@app.route('/api/data', methods=['GET'])
def get_data():
    """获取疫情数据接口"""
    data = get_all_data()
    return jsonify({
        'status': 'success',
        'data': data
    })

@app.route('/')
def index():
    """首页路由"""
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

3. ECharts前端实现(index.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>疫情监控系统</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
    <style>
        body { margin: 0; }
        #main { width: 100vw; height: 100vh; }
    </style>
</head>
<body>
    <div id="main"></div>
    <script>
        // 获取数据
        fetch('/api/data')
            .then(response => response.json())
            .then(data => {
                // 处理数据
                const seriesData = data.map(item => ({
                    name: item.region,
                    value: [item.confirmed, item.cured, item.dead]
                }));
                
                // 创建图表
                const chart = echarts.init(document.getElementById('main'));
                const option = {
                    title: {
                        text: '疫情统计'
                    },
                    tooltip: {
                        trigger: 'axis'
                    },
                    xAxis: {
                        type: 'category',
                        data: seriesData.map(d => d.name)
                    },
                    yAxis: {
                        type: 'value'
                    },
                    series: [{
                        name: '确诊',
                        type: 'bar',
                        data: seriesData.map(d => d.value[0])
                    }, {
                        name: '治愈',
                        type: 'bar',
                        data: seriesData.map(d => d.value[1])
                    }, {
                        name: '死亡',
                        type: 'bar',
                        data: seriesData.map(d => d.value[2])
                    }]
                };
                
                chart.setOption(option);
            });
    </script>
</body>
</html>

五、完整案例

1. 项目运行流程

  1. 启动爬虫服务(可单独运行crawler.py)
  2. 启动Flask应用(运行app.py)
  3. 访问 http://localhost:5000 查看实时数据
  4. ECharts会自动加载最新数据并渲染图表

2. 系统功能演示

  • 自动爬取:每小时自动更新数据
  • 多维度展示:同时显示确诊/治愈/死亡数据
  • 实时更新:页面刷新后自动获取最新数据
  • 数据持久化:所有历史数据存储在SQLite中

3. 系统扩展性

  • 可添加趋势分析模块(折线图)
  • 可增加地图可视化(geo组件)
  • 可集成预警系统(阈值检测)

六、源码解析

1. 爬虫代码分析

# 随机User-Agent生成
def get_random_user_agent():
    agents = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4443.116 Safari/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/91.0.4443.116 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4443.116 Safari/537.36"
    ]
    return random.choice(agents)

关键点:使用随机User-Agent防止被封IP

2. 数据库优化

# 查询优化示例
def get_latest_data(limit=10):
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    cursor.execute('''
        SELECT * FROM stats 
        ORDER BY timestamp DESC 
        LIMIT ?
    ''', (limit,))
    rows = cursor.fetchall()
    conn.close()
    return rows

说明:通过LIMIT限制返回数据量,提升查询效率

3. 前端数据处理

// 数据处理逻辑
const seriesData = data.map(item => ({
    name: item.region,
    value: [item.confirmed, item.cured, item.dead]
}));

说明:将原始数据转换为ECharts支持的格式

七、进阶使用

1. 数据持久化改进

使用SQLite的索引优化查询:

# 创建索引
cursor.execute('''
    CREATE INDEX IF NOT EXISTS idx_timestamp 
    ON stats (timestamp)
''')

2. 增加预警功能

# 预警逻辑示例
def check_alerts(data):
    for item in data:
        if item['confirmed'] > 10000:
            print(f"警报:{item['region']}确诊超1万")

3. 前端交互增强

// 添加动态更新
setInterval(() => {
    fetch('/api/data')
        .then(response => response.json())
        .then(data => {
            // 更新图表
        });
}, 60000); // 每分钟刷新一次

八、性能与工程实践

1. 性能优化方案

优化项方法效果
爬虫优化使用多线程/异步提升爬取效率
数据库使用索引/分页查询加快数据检索
缓存机制使用Redis缓存热点数据降低接口响应时间
前端优化使用Web Worker提升图表渲染性能

2. 异常处理机制

# 异常处理示例
def safe_fetch_data():
    try:
        return fetch_data()
    except requests.exceptions.RequestException as e:
        print(f"网络错误: {str(e)}")
        return []
    except Exception as e:
        print(f"未知错误: {str(e)}")
        return []

3. 安全考虑

  • 前端接口需添加身份验证(推荐JWT)
  • 爬虫需遵守robots.txt规则
  • 数据库连接使用环境变量配置
  • 对用户输入进行校验和过滤

九、常见问题与踩坑

1. 爬虫被封IP问题

错误现象:爬虫运行后频繁失败

解决方法:

  • 使用代理IP池
  • 增加请求间隔
  • 使用更真实的User-Agent
# 增加请求间隔
time.sleep(random.randint(1, 5))

2. 数据解析错误

错误现象:数据字段缺失导致程序崩溃

解决方法:

  • 添加字段存在性校验
  • 使用default参数处理缺失值
# 字段校验示例
def safe_get_text(tag, default='0'):
    return tag.text.strip() if tag else default

3. 接口响应慢

错误现象:页面加载卡顿

解决方法:

  • 使用缓存机制
  • 优化SQL查询
  • 前端异步加载
# 缓存示例
from functools import lru_cache

@lru_cache(maxsize=100)
def get_cached_data():
    return get_all_data()

十、最佳实践

1. 推荐使用场景

  • 需要实时监控的中小型项目
  • 数据源更新频率较低的系统
  • 对可视化要求不高的监控场景
  • 需要快速搭建的原型系统

2. 不推荐使用场景

  • 需要处理海量数据的系统
  • 需要高并发访问的平台
  • 数据源需要复杂处理的场景
  • 需要严格安全控制的系统

3. 推荐实践方案

  • 使用Celery实现异步爬虫
  • 使用Gunicorn部署Flask应用
  • 使用Redis缓存热点数据
  • 使用Docker容器化部署
  • 使用Prometheus+Grafana监控系统

十一、总结

基于Flask+ECharts+爬虫的疫情监控系统是一个典型的前后端分离架构案例。通过爬虫获取数据,Flask处理业务逻辑,ECharts实现可视化,形成完整的数据处理闭环。系统在实际应用中需要注意:

  1. 爬虫策略要符合网站规则,避免被封
  2. 数据处理要严谨,防止脏数据
  3. 前端交互要友好,提升用户体验
  4. 系统要具备扩展性,方便后续升级

虽然该方案适合中小型项目,但在处理复杂业务时需要考虑更专业的技术栈(如Django+DRF+GraphQL)。对于需要处理大规模数据或高并发的场景,建议采用分布式架构和更专业的数据处理方案。

最后修改于:2026年09月27日 07:52

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日