【疫情监控】打造全国新冠疫情Web项目:三次优化,提升用户体验

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 本文分享了开发全国新冠疫情Web项目的经验和优化过程,包括调整项目结构、新增logger模块改善日志记录、实现数据实时刷新功能,以及通过spider模块代码自动更新疫情数据,提升了用户体验并简化了项目维护流程。

日拱一卒无有尽,功不唐捐终入海!

一、背景

在整理公司项目的时候 ,不晓得什么时候接了一个关于疫情的项目,需要可视化展示数据 ,大致了解过项目结构和实现逻辑,先是从公共接口爬取数据,然后清洗数据再写入数据库,最后使用flask后台框架启动访问web,读取数据库的数据,经过前端渲染,如下图所示:

在这里插入图片描述

1.1、调整原来的项目结构

在这里插入图片描述

新增了conf目录,作为该项目的数据配置项,譬如mysql信息就统一管理,因为原项目在spider和sqldata两处都创建了mysql连接,所以只保留一处即可

def mysql():
    """创建mysql连接,返回数据库操作对象"""
    db = pymysql.connect(host=MYSQL_HOST, user=MYSQL_DB_USER, password=MYSQL_DB_PASSWORD, database=MYSQL_DB_NAME, charset='utf8')
    cur = db.cursor()
    return db, cur


def close(db, cur):
    """关闭数据库连接"""
    cur.close()
    db.close()


def query(sql: str, *args):
    """执行sql并返回结果"""
    db, cur = mysql()
    cur.execute(sql, args)
    result = cur.fetchall()
    close(db, cur)
    return result

二、新增/优化功能

原项目中在spider文件中,每次操作都会在控制台输出print日志,这个样子有点不美观

在这里插入图片描述

2.1、新增logger模块

创建libs作为整个项目的工具类

class Logger(object):
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.__instance: 
            cls.__instance = object.__new__(cls, *args) 
        return cls.__instance 

    def __init__(self):
        self.formater = logging.Formatter(
            '[%(asctime)s] [%(levelname)s] [%(module)s:%(lineno)d ], %(message)s')
        self.logger = logging.getLogger('log')
        self.logger.setLevel(logging.DEBUG)
        self.filelogger = handlers.RotatingFileHandler(LOG_PATH,
                                                       maxBytes=5242880,
                                                       backupCount=3,
                                                       encoding='utf-8')
        # self.console = logging.StreamHandler()
        # self.console.setLevel(logging.DEBUG)
        self.filelogger.setFormatter(self.formater)
        # self.console.setFormatter(self.formater)
        self.logger.addHandler(self.filelogger)
        # self.logger.addHandler(self.console)
  • 日志输出乱码?

在文件处理对象的时候,需要指定encoding=‘utf-8’,避免乱码

2.2、实时刷新数据

前面读了spider模块,每次执行都会先删除表然后再创建最后写入数据,从数据库配置来看完全没有任何压力,但是启动app时,它并没有刷新数据库,每次都会获取第一次执行spider的数据,所以需要做到实时刷新,也就是spider需要每次都执行。

@app.route('/')
def hello_world():
    logger.logger.info("用来刷新数据...")
    main() # 这个就是每次操作数据库,删除、创建、写入
    return render_template('main.html')

三、总结

就整个项目而言,原先是需要先执行spider然后再启动app,所以没那么智能,在经过调整后,只需要启动app就会连带讲数据库的数据刷新一遍,每次都会是最新数据,尽管是每天的某个固定时间才会更新,总好过每次手动指定spider模块;

  • 还有一点优化,就是requirements.txt;在当前环境安装pipreqs,然后在工程根路径下执行pipreqs ./就可以获得项目相关的库依赖,这样就方便项目迁移了。

附录spider模块代码<关注我,获取源码>:

def get_json(url):
    headers = {
   
   
        'user - agent': 'Mozilla / 5.0(WindowsNT10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 80.0.3987.116Safari / 537.36'
    }
    response = requests.get(url=url, headers=headers).text
    data = json.loads(response)
    return data['data']


# 获取当日数据
def get_now(data):
    now = []
    data_time = str(data['diseaseh5Shelf']['lastUpdateTime'])  # 数据更新时间
    data_all = data['diseaseh5Shelf']['areaTree'][0]
    data_province_s = data['diseaseh5Shelf']['areaTree'][0]['children']

    # 获取全国今日新增、累计确诊、治愈人数、死亡人数
    confirms = data_all['total']['confirm']
    confirms_add = data_all['today']['confirm']
    heals = data_all['total']['heal']
    deads = data_all['total']['dead']

    # 获取每个省份的每个城市今日新增、累计确诊、治愈人数、死亡人数
    for data_province in data_province_s:
        province = data_province['name']  # 省份
        for data_city in data_province['children']:
            city = data_city['name']  # 城市
            confirm = data_city['total']['confirm']  # 确诊
            confirm_add = data_city['today']['confirm']  # 新增
            heal = data_city['total']['heal']  # 治愈
            dead = data_city['total']['dead']  # 死亡
            now.append((data_time, province, city, confirm_add, confirm, heal, dead))
    return confirms, confirms_add, heals, deads, now


# 获取历史数据
def get_past(data):
    past = {
   
   }
    for data_day in data:
        data_time = data_day['date']  # 获取最原始的时间
        time_deal = time.strptime(data_time, '%m.%d')  # 根据指定的格式把一个时间字符串解析为时间元组
        date = time.strftime('%m-%d', time_deal)  # 重新组成新的时间字符串
        past[date] = {
   
   
            'confirm': data_day['confirm'],  # 确诊
            'suspect': data_day['suspect'],  # 疑似
            'heal': data_day['heal'],  # 治愈
            'dead': data_day['dead']  # 死亡
        }

    return past


# 写入当日数据
def insert_now(now):
    try:
        logger.logger.info("每次这个很有意思,每次刷新都需要先删除数据表,然后再创建表,最后再将清洗的数据写入表中.")
        cur.execute("DROP TABLE IF EXISTS 当日数据")
        # 写创建表的sql语句
        set_sql_now = "create table 当日数据(时间 varchar(100),省份 varchar(50),城市 varchar(50),新增确诊 int(11)," \
                      "确诊人数 int(11),治愈人数 int(11),死亡人数 int(11))ENGINE=InnoDB DEFAULT CHARSET=utf8"
        # 执行sql语句
        cur.execute(set_sql_now)
        # 保存
        db.commit()
        # 写入数据库
        save_sql_now = "insert into 当日数据 values(%s,%s,%s,%s,%s,%s,%s)"
        cur.executemany(save_sql_now, now)  # now位置必须是个列表,列表里面的元素是数组
        db.commit()
        logger.logger.info('当日数据写入成功')
    except Exception as e:
        logger.logger.error('当日数据写入失败原因:%s' % e)


# 写入历史数据
def insert_past(past):
    try:
        cur.execute("DROP TABLE IF EXISTS 历史数据")
        # 写创建表的sql语句
        set_sql_past = "create table 历史数据(时间 varchar(100),确诊人数 int(11),疑似病例 int(11),治愈人数 int(11)," \
                       "死亡人数 int(11))ENGINE=InnoDB DEFAULT CHARSET=utf8"
        # 执行sql语句
        cur.execute(set_sql_past)
        # 保存
        db.commit()
        # 写入历史数据
        for date, data in past.items():
            sql_past = f"insert into 历史数据 values('{date}',{data['confirm']},{data['suspect']},{data['heal']}," \
                       f"{data['dead']})"
            cur.execute(sql_past)
        db.commit()
        logger.logger.info('历史数据写入成功')
    except Exception as e:
        logger.logger.error('历史数据写入失败原因:%s' % e)


# 写入历史新增数据
def insert_past_add(past):
    try:
        cur.execute("DROP TABLE IF EXISTS 历史新增数据")
        # 写创建表的sql语句
        set_sql_past = "create table 历史新增数据(时间 varchar(100),新增确诊 int(11),新增疑似 int(11),新增治愈 int(11)," \
                       "新增死亡 int(11))ENGINE=InnoDB DEFAULT CHARSET=utf8"
        # 执行sql语句
        cur.execute(set_sql_past)
        # 保存
        db.commit()
        # 写入历史数据
        for date, data in past.items():
            sql_past = f"insert into 历史新增数据 values('{date}',{data['confirm']},{data['suspect']},{data['heal']}," \
                       f"{data['dead']})"
            cur.execute(sql_past)
        db.commit()
        logger.logger.info('历史新增数据写入成功')
    except Exception as e:
        logger.logger.error('历史新增数据写入失败原因:%s' % e)


# 写入累计确诊等四个关键数据
def insert_main(confirm, confirm_add, heal, dead):
    try:
        cur.execute("DROP TABLE IF EXISTS 关键数据")
        # 写创建表的sql语句
        set_sql_main = "create table 关键数据(确诊人数 int(11),新增确诊 int(11),治愈人数 int(11),死亡人数 int(11))" \
                       "ENGINE=InnoDB DEFAULT CHARSET=utf8"
        cur.execute(set_sql_main)
        db.commit()
        # 写入确诊人数、新增确诊、治愈人数、死亡人数的数据
        save_sql_main = f"insert into 关键数据 values({confirm},{confirm_add},{heal},{dead})"
        cur.execute(save_sql_main)
        db.commit()
        logger.logger.info('关键数据写入成功')
    except Exception as e:
        logger.logger.error('关键数据写入失败原因:%s' % e)
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Web App开发 前端开发 JavaScript
探索Python科学计算的边界:利用Selenium进行Web应用性能测试与优化
【10月更文挑战第6天】随着互联网技术的发展,Web应用程序已经成为人们日常生活和工作中不可或缺的一部分。这些应用不仅需要提供丰富的功能,还必须具备良好的性能表现以保证用户体验。性能测试是确保Web应用能够快速响应用户请求并处理大量并发访问的关键步骤之一。本文将探讨如何使用Python结合Selenium来进行Web应用的性能测试,并通过实际代码示例展示如何识别瓶颈及优化应用。
97 5
|
12天前
|
JavaScript 前端开发 开发工具
web项目规范配置(husky、eslint、lint-staged、commit)
通过上述配置,可以确保在Web项目开发过程中自动进行代码质量检查和规范化提交。Husky、ESLint、lint-staged和Commitlint共同作用,使得每次提交代码之前都会自动检查代码风格和语法问题,防止不符合规范的代码进入代码库。这不仅提高了代码质量,还保证了团队协作中的一致性。希望这些配置指南能帮助你建立高效的开发流程。
30 5
|
19天前
|
JavaScript 前端开发 数据安全/隐私保护
Web开发者必看:手把手教你如何轻松播放m3u8流地址,解锁视频播放新技能,让你的项目更上一层楼!
【10月更文挑战第23天】随着互联网技术的发展,m3u8格式因良好的兼容性和高压缩率被广泛用于网络流媒体传输。本文介绍如何在Web端播放m3u8流地址,包括引入视频播放器(如Video.js)、创建播放器容器、初始化播放器及播放m3u8流的具体步骤。此外,还涉及处理加密m3u8流的示例。
68 1
|
26天前
|
缓存 API UED
通过渐进式Web应用(PWA)提升用户体验
【10月更文挑战第15天】渐进式Web应用(PWA)结合了传统Web应用和移动应用的优点,提供更快、更可靠和更吸引人的用户体验。本文介绍PWA的核心特性、优势及构建方法,包括服务工作线程、响应式设计和现代Web API的应用,帮助开发者提升用户体验。
|
25天前
|
机器学习/深度学习 缓存 监控
利用机器学习优化Web性能和用户体验
【10月更文挑战第16天】本文探讨了如何利用机器学习技术优化Web性能和用户体验。通过分析用户行为和性能数据,机器学习可以实现动态资源优化、预测性缓存、性能瓶颈检测和自适应用户体验。文章还介绍了实施步骤和实战技巧,帮助开发者更有效地提升Web应用的速度和用户满意度。
|
27天前
|
JSON 搜索推荐 API
Python的web框架有哪些?小项目比较推荐哪个?
【10月更文挑战第15天】Python的web框架有哪些?小项目比较推荐哪个?
45 1
|
1月前
|
缓存 前端开发 JavaScript
探索现代Web开发中的前端性能优化策略
【10月更文挑战第5天】探索现代Web开发中的前端性能优化策略
|
1月前
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
120 0
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
100 3
|
15天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
101 44