效果图

截图:
image.png

视频: Video_2020-02-02_193647.wmv (6.59MB)

数据来源

如何查找数据

目前各大网站(BAT)都重点关注全国疫情情况,可以通过Chrome/Firefox开发者工具,去捕获查找,抓包都可以
本次的小demo我是直接使用天行API,目前免费注册,疫情数据API免费使用不限次数,做做练手比较方便,不用担心被封IP之类的情况发生啦。

如果不会找数据API的话,可以看看下面链接文章:

https://blog.csdn.net/msssssss/article/details/104109503 Python实战:抓肺炎疫情实时数据,画2019-nCoV疫情地图
https://www.bilibili.com/read/cv4485550/ 制作一个疫情地图(html+node)
https://mp.weixin.qq.com/s/DjBIu4851l0a_vN_aEjo7Q 如何用 Python 画出新型冠状病毒疫情地图?

API数据分析-地方市

天行API的数据也是来自丁香园的数据,一共有2个API分别,地方市情况、省份情况,两个数据有时候不同步,甚至有点差别,使用的时候就细心的看看哈。

详细的就不说太多了,说了没有意义,多点去发掘问题解决问题,才是体验你为何掉头发的根本原因啊!

新闻列表,各地方市情况,全国情况数据分析,下列是我认为比较需要的数据,整理了一下,仅供参考,按照自己的实际情况处理即可。
image.png

新闻列表字段 各地市情况字段 全国情况字段
id id id
pubDate 发布时间 createTime createTime
pubDateStr xxx前发布 modifyTime modifyTime
title tags infectSource
summary 新闻简介 provinceId passWay
infoSource provinceName imgUrl 地图
sourceUrl provinceShortName dailyPic 情况趋势图
provinceId cityName summary
provinceName confirmedCount 确诊数 virus
createTime suspectedCount 疑似数 remark1 感染情况
modifyTime curedCount 治愈数 remark2 感染症状
deadCount 死亡数 generalRemark 数据来源说明
comment 备注

API数据分析-省份

省份列表数据,
image.png

provinceName
provinceShortName
confirmedCount 确诊数
suspectedCount 疑似数
curedCount 治愈数
deadCount 死亡数
comment 备注
cities 各城市情况,(字典 列表 字典嵌套)

数据处理-python

人生苦短,我用python
API拿到的都是json,简单用python处理一下就好,反正就一句话,json,字典,列表,这些类型的数据直接for一下就好了,没有for不能处理的东西,只是你还没有想到for多少次而是,哪里不死就往死里弄,反正你要的数据总会来的,时间就是最好的经验了

数据处理-目录结构

image.png

数据处理-思路分析

虽然API都是免费的,如果每次发起请求的话,操作比较慢,暂用资源,造成不必要的浪费,所以总结了一下

访问API 本地:省时,远程:消耗服务器
requests包
接收数据,写入为本地数据json 轻量级存储,够用
直接读取本都数据json python 处理方便
对本地数据json处理 for循环就好了 、json包
处理后的数据写入MySQL inset ,update基本sql语句、pymysql包
FineBI读取MySQL数据,生成可视化图表

数据处理-公共类

总结:
怎样方便就怎样写,常用的,多次用的,很少有改变的,固定的写法,就先推在一起吧!
只要思想不滑坡,方法总比困难多!

  1. import json
  2. import pymysql
  3. class BaseFun(object):
  4. @staticmethod
  5. def conn_database(data_name):
  6. """
  7. 连接数据库方法
  8. :param data_name: 传入-数据库名称
  9. :return: 连接后返回,conn
  10. """
  11. conn = pymysql.connect(
  12. host='localhost',
  13. user='root',
  14. password='root',
  15. database=data_name,
  16. charset='utf8')
  17. print("连接数据库成功", conn.cursor())
  18. return conn
  19. @staticmethod
  20. def city_url():
  21. """
  22. 各市级疫情数据api,只有城市情况
  23. :return:city_url
  24. """
  25. city_url = 'http://api.tianapi.com/txapi/ncovcity/index?key=替换你自己的key'
  26. print('连接【各市级】疫情数据api,成功')
  27. return city_url
  28. @staticmethod
  29. def prov_url():
  30. """
  31. 各省级疫情数据api,包含新闻,全汇总
  32. :return: prov_rul
  33. """
  34. prov_rul = 'http://api.tianapi.com/txapi/ncov/index?key=替换你自己的key'
  35. print('连接【各省级】疫情数据api,成功')
  36. return prov_rul
  37. @staticmethod
  38. def read_json(file_path):
  39. with open(file_path, 'r', encoding='utf-8') as f:
  40. res_data = json.load(f)
  41. print('读取数据成功')
  42. return res_data
  43. @staticmethod
  44. def write_json(file_path, data):
  45. with open(file_path, 'w', encoding='utf-8')as f:
  46. json.dump(data, f)
  47. print('写入数据成功')

image.png

数据处理-全省数据

总结:
开始不停的调用公共类的方法,不停的for去处理数据
比较特别就是main的使用,和BASE_DIR路径处理,原因是数据存放的文件夹ncov_v2\data,直接在pycharm运行没有问题,如果在黑窗口运行就会报错,读取不了数据,原因是路径问题

  1. # 获取当前文件的上一层绝对路径
  2. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  1. import requests
  2. from base.conn_base import BaseFun
  3. from utlis import BASE_DIR
  4. data_list = list()
  5. class City(BaseFun):
  6. url = BaseFun.city_url()
  7. def __init__(self):
  8. self.res_data = requests.get(self.url)
  9. self.response_data = self.res_data.json()
  10. self.conn = BaseFun.conn_database('ncov')
  11. BaseFun.write_json(BASE_DIR + '/data/api_province_v2.json', self.response_data)
  12. def read_data(self):
  13. res_data = BaseFun.read_json(BASE_DIR + '/data/api_province_v2.json')
  14. return res_data['newslist']
  15. def data_4_list(self):
  16. for res in self.read_data():
  17. data_list.append([
  18. res.get('provinceName'),
  19. res.get('provinceShortName'),
  20. res.get('confirmedCount'),
  21. res.get('suspectedCount'),
  22. res.get('curedCount'),
  23. res.get('deadCount'),
  24. res.get('comment'),
  25. str(res.get('cities')),
  26. ])
  27. def update_sql(self):
  28. for i in range(len(data_list)):
  29. self.sql = "update api_province_v2 set provinceName=%s, provinceShortName=%s, confirmedCount=%s, suspectedCount=%s, curedCount=%s, deadCount=%s, comment=%s, cities=%s where provinceShortName=" + '"' + \
  30. data_list[i][1] + '"'
  31. # 获取一个光标
  32. self.conn.cursor()
  33. # 连接并执行
  34. self.conn.cursor().execute(self.sql, [data_list[i][0], data_list[i][1], data_list[i][2], data_list[i][3],
  35. data_list[i][4],
  36. data_list[i][5], data_list[i][6], data_list[i][7]])
  37. # 涉及写操作注意要提交
  38. self.conn.commit()
  39. # 关闭光标对象
  40. self.conn.cursor().close()
  41. # 关闭数据库连接
  42. self.conn.close()
  43. if __name__ == '__main__':
  44. update_data = City()
  45. update_data.data_4_list()
  46. update_data.update_sql()
  47. print('数据更新成功!')

image.png

数据处理-新闻列表数据

总结:
写法和上一个差不多,思路都是一样的
唯一的就是这里,我写了一个小陷阱,哈哈哈
重点好似没有什么影响

  1. import requests
  2. from base.conn_base import BaseFun
  3. from utlis import BASE_DIR, time_change
  4. data_list = list()
  5. class News(BaseFun):
  6. url = BaseFun.prov_url()
  7. def __init__(self):
  8. self.res_data = requests.get(self.url)
  9. self.response_data = self.res_data.json()
  10. self.conn = BaseFun.conn_database('ncov')
  11. BaseFun.write_json(BASE_DIR + '/data/api_desc.json', self.response_data)
  12. def read_data(self):
  13. res_data = BaseFun.read_json(BASE_DIR + '/data/api_desc.json')
  14. return res_data['newslist'][0]['news']
  15. def insets_sql(self):
  16. for res in self.read_data():
  17. if 'provinceName' in res.keys():
  18. # 更新sql语句
  19. sql = "INSERT INTO api_news (id, pubDate, pubDateStr, title, summary,infoSource,sourceUrl, provinceId, provinceName, createTime,modifyTime) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON DUPLICATE KEY UPDATE id = id"
  20. # 获取一个光标
  21. cursor = self.conn.cursor()
  22. # 连接并执行
  23. cursor.execute(sql, [res['id'], time_change(res['pubDate']), res['pubDateStr'], res['title'],
  24. res['summary'], res['infoSource'], res['sourceUrl'], res['provinceId'],
  25. res['provinceName'], time_change(res['createTime']),
  26. time_change(res['modifyTime'])])
  27. else:
  28. sql = "INSERT INTO api_news (id, pubDate, pubDateStr, title, summary,infoSource,sourceUrl, provinceId, createTime, modifyTime) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON DUPLICATE KEY UPDATE id = id"
  29. # 获取一个光标
  30. cursor = self.conn.cursor()
  31. cursor.execute(sql, [res['id'], time_change(res['pubDate']), res['pubDateStr'], res['title'],
  32. res['summary'], res['infoSource'], res['sourceUrl'], res['provinceId'],
  33. time_change(res['createTime']),
  34. time_change(res['modifyTime'])])
  35. # 涉及写操作注意要提交
  36. self.conn.commit()
  37. # 关闭光标对象
  38. cursor.close()
  39. # 关闭数据库连接
  40. self.conn.close()
  41. if __name__ == '__main__':
  42. insets_data = News()
  43. insets_data.insets_sql()
  44. print('数据更新成功!')

数据处理-全国情况数据

总结:
增加一个ccTime的数字字段,存入当天的年月日,确保当日只有一条数据,desc字段的数据有更新,具体下面有说明,增加了较昨日对比的数据

  1. from datetime import datetime
  2. import requests
  3. from base.conn_base import BaseFun
  4. from utlis import BASE_DIR, time_change
  5. data_list = list()
  6. now = datetime.now()
  7. formatted_date = now.strftime('%Y-%m-%d')
  8. class Desc(BaseFun):
  9. url = BaseFun.prov_url()
  10. def __init__(self):
  11. self.res_data = requests.get(self.url)
  12. self.response_data = self.res_data.json()
  13. self.conn = BaseFun.conn_database('ncov')
  14. BaseFun.write_json(BASE_DIR + '/data/api_desc.json', self.response_data)
  15. def read_data(self):
  16. res_data = BaseFun.read_json(BASE_DIR + '/data/api_desc.json')
  17. return res_data['newslist'][0]['desc']
  18. def data_4_list(self):
  19. for res in self.read_data():
  20. data_list.append(self.read_data()[res])
  21. print(data_list)
  22. def insets_sql(self):
  23. sql = "INSERT INTO api_desc (createTime,modifyTime,infectSource,passWay,imgUrl,dailyPic,countConfirmedCount,countSuspectedCount,countCuredCount,countDeadCount,virus,remark1,remark2,generalRemark,ccTime) " \
  24. "VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) " \
  25. "ON DUPLICATE KEY UPDATE ccTime=%s,modifyTime=%s,countConfirmedCount=%s,countSuspectedCount=%s,countCuredCount=%s,countDeadCount=%s"
  26. # 获取一个光标
  27. self.conn.cursor()
  28. # 连接并执行
  29. int_val = (time_change(data_list[1]), time_change(data_list[2]), data_list[3], data_list[4],
  30. data_list[5], data_list[6], data_list[10], data_list[11], data_list[12], data_list[13],
  31. data_list[20], data_list[21], data_list[22],
  32. data_list[26], formatted_date)
  33. upd_val = (formatted_date, time_change(data_list[2]), data_list[10],
  34. data_list[11], data_list[12], data_list[13])
  35. val = (*int_val, *upd_val)
  36. self.conn.cursor().execute(sql, val)
  37. # 涉及写操作注意要提交
  38. self.conn.commit()
  39. # 关闭光标对象
  40. self.conn.cursor().close()
  41. # 关闭数据库连接
  42. self.conn.close()
  43. if __name__ == '__main__':
  44. insets_data = Desc()
  45. insets_data.data_4_list()
  46. insets_data.insets_sql()
  47. print('数据更新成功!')

API的数据新增,seriousCount严重病例,和较昨日对比数据字段suspectedIncr,confirmedIncr,curedIncr,deadIncr,seriousIncr
image.png

数据处理-地方市数据

总结:
地方市的数据来自,省份数据cities字段,所以一开始直接读取省份json,进行数据重新整理,并生成新的city.json,执行读写和写入数据库

  1. from base.conn_base import BaseFun
  2. from utlis import BASE_DIR
  3. city_list = list()
  4. class City(BaseFun):
  5. def __init__(self):
  6. self.conn = BaseFun.conn_database('ncov')
  7. self.res_data = BaseFun.read_json(BASE_DIR + '/data/api_province_v2.json')
  8. def data_4_list(self):
  9. for i in self.res_data['newslist']:
  10. for j in i['cities']:
  11. # 城市名称列表
  12. # print(j)
  13. city_list.append(j)
  14. print('本地数据处理成功!')
  15. def write_data(self):
  16. BaseFun.write_json(BASE_DIR + '/data/api_city.json', city_list)
  17. def read_data(self):
  18. res_datas = BaseFun.read_json(BASE_DIR + '/data/api_city.json')
  19. return res_datas
  20. def update_sql(self):
  21. for res in self.read_data():
  22. sql = "update api_city set confirmedCount=%s, suspectedCount=%s, curedCount=%s, deadCount=%s where cityName =%s"
  23. # 获取一个光标
  24. self.conn.cursor()
  25. # 连接并执行
  26. self.conn.cursor().execute(sql, [res['confirmedCount'], res['suspectedCount'], res['curedCount'],
  27. res['deadCount'],
  28. res['cityName']])
  29. # 涉及写操作注意要提交
  30. self.conn.commit()
  31. # 关闭光标对象
  32. self.conn.cursor().close()
  33. # 关闭数据库连接
  34. self.conn.close()
  35. if __name__ == '__main__':
  36. update_data = City()
  37. update_data.data_4_list()
  38. update_data.write_data()
  39. update_data.read_data()
  40. update_data.update_sql()
  41. print('数据更新成功!')

image.png

数据处理-统一调动

总结:
每个py文件都需要单独执行一次,才能写入数据到数据库,为了方便每个api*.py都增加一个start方法自调用,根目录增加一个start.py调动所有api接口

  1. from api_city import City
  2. from api_desc import Desc
  3. from api_news import News
  4. from api_province import Prov
  5. class Start(object):
  6. def __init__(self):
  7. Prov().start()
  8. print('全省数据更新-成功')
  9. News().start()
  10. print('新闻数据更新-成功')
  11. Desc().start()
  12. print('全国数据更新-成功')
  13. City().start()
  14. print('全市数据更新-成功')
  15. print('--------------')
  16. print('全数据更新-成功')
  17. if __name__ == '__main__':
  18. Start()

image.png

数据处理-MySQL

黑窗口操作太难了,直接使用Navicat操作数据库吧!

数据处理-目录结构SQL

image.png

数据处理-全省数据SQL

总结:provinceId,provinceName需要自行添加这个来个字段的数据,否则后期数据可视化无法实现联动显示

  1. CREATE TABLE `api_province_v2` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `provinceId` int(11) DEFAULT NULL,
  4. `provinceName` varchar(15) DEFAULT NULL,
  5. `provinceShortName` varchar(15) DEFAULT NULL,
  6. `confirmedCount` int(11) DEFAULT NULL,
  7. `suspectedCount` int(11) DEFAULT NULL,
  8. `curedCount` int(11) DEFAULT NULL,
  9. `deadCount` int(11) DEFAULT NULL,
  10. `comment` varchar(255) DEFAULT NULL,
  11. `cities` text,
  12. PRIMARY KEY (`id`)
  13. ) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;

image.png

数据处理-新闻列表数据SQL

总结:
还是上面python处理的时候那个坑,哈哈哈

  1. CREATE TABLE `api_news` (
  2. `id` int(11) NOT NULL,
  3. `pubDate` varchar(255) DEFAULT NULL,
  4. `pubDateStr` varchar(255) DEFAULT NULL,
  5. `title` varchar(255) DEFAULT NULL,
  6. `summary` text,
  7. `infoSource` varchar(255) DEFAULT NULL,
  8. `sourceUrl` text,
  9. `provinceId` varchar(11) DEFAULT NULL,
  10. `provinceName` varchar(255) DEFAULT NULL,
  11. `createTime` varchar(255) DEFAULT NULL,
  12. `modifyTime` varchar(255) DEFAULT NULL,
  13. PRIMARY KEY (`id`)
  14. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

image.png

数据处理-全国情况数据SQL

总结:
每天只有一条总数据,ccTime

  1. CREATE TABLE `api_desc` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `createTime` varchar(255) DEFAULT NULL,
  4. `modifyTime` varchar(255) DEFAULT NULL,
  5. `infectSource` varchar(255) DEFAULT NULL,
  6. `passWay` varchar(255) DEFAULT NULL,
  7. `imgUrl` varchar(255) DEFAULT NULL,
  8. `dailyPic` varchar(255) DEFAULT '',
  9. `summary` varchar(255) DEFAULT NULL,
  10. `deleted` varchar(255) DEFAULT NULL,
  11. `countRemark` varchar(255) DEFAULT NULL,
  12. `countConfirmedCount` varchar(255) DEFAULT NULL,
  13. `countSuspectedCount` varchar(255) DEFAULT NULL,
  14. `countCuredCount` varchar(255) DEFAULT NULL,
  15. `countDeadCount` varchar(255) DEFAULT NULL,
  16. `virus` varchar(255) DEFAULT NULL,
  17. `remark1` varchar(255) DEFAULT NULL,
  18. `remark2` varchar(255) DEFAULT NULL,
  19. `remark3` varchar(255) DEFAULT NULL,
  20. `remark4` varchar(255) DEFAULT NULL,
  21. `remark5` varchar(255) DEFAULT NULL,
  22. `generalRemark` varchar(255) DEFAULT NULL,
  23. `ccTime` varchar(255) NOT NULL,
  24. PRIMARY KEY (`id`,`ccTime`),
  25. UNIQUE KEY `ctime` (`ccTime`)
  26. ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;

image.png

数据处理-地方市数据SQL

总结:
provinceId,provinceName联动字段和全省的数据关联

  1. CREATE TABLE `api_city` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `cityName` varchar(255) DEFAULT NULL,
  4. `confirmedCount` varchar(255) DEFAULT NULL,
  5. `suspectedCount` varchar(255) DEFAULT NULL,
  6. `curedCount` varchar(255) DEFAULT NULL,
  7. `deadCount` varchar(255) DEFAULT NULL,
  8. `provinceId` int(11) DEFAULT NULL,
  9. `provinceName` varchar(255) DEFAULT NULL,
  10. PRIMARY KEY (`id`)
  11. ) ENGINE=InnoDB AUTO_INCREMENT=332 DEFAULT CHARSET=utf8;

image.png

数据应用

嗯嗯,过年啊,只能在家里,都快要长蘑菇了,看见哪些一张张的大数据,手有点痒,在x度看了一下数据可视化,然鹅有很多开源和免费的可视化工具,选了FineBI,就是免费、模板多可以满足目前的需求,而且简单~~

数据可视化-安装

这里不说,建议你看官方文档

数据可视化-启动

安装完直接双击FineBI启动
启动过程:
image.png

启动完成:
image.png

数据可视化-数据载入

  1. FineBI登录成功之后,第一步创建数据

image.png

  1. 添加本地数据

image.png

  1. 对数据表的字段进行设置,数值,日期优先作为数据指标,该设置不影响真实数据库

修改字段:
image.png

最终字段:
image.png

数据可视化-组件创建

  1. 数据准备好,点击右上角【创建组件】,官方文档有教程的

image.png

  1. 将准备好数据字段拖动到对应的位置

image.png

数据可视化-组件联动

  1. 设置联动,如果数据来自同一个数据表会自己联动的,如果来自其他数据就需要自定处理,关联一下,类似MySQL的inner join的用法

image.png

  1. 自定义联动设置(有勾选的代表已经联动)

image.png

总结:

完!
python代码已经上传git
https://github.com/tommy-it/2019-nCov-Map.git