Django搭建的疫情数据可视化

目录

摘要

3.2数据可视化

3.2.1疫情地图

3.2.2趋势图

3.3部署django

3.3.1创建项目、app

3.3.2编写urls.py

3.3.3编写views.py

3.3.4编写html文件

3.3.5包含一些js、css文件

4.1疫情地图

4.2疫情趋势图


摘要

搭建基于Django框架的前后端系统,后端的主要功能为获取数据、保存数据、可视化数据,将可视化内容合成成网页;客户端通过浏览器访问,查看疫情地图、疫情趋势图。后端软件从相关网站获取每日最新的疫情数据,对数据进行正则化处理,提取需要的疫情数据,保存为json格式的文件;调用可视化模块pyechart,制作成可视化内容;编写响应的html文件,达到数据可视化的目的。

1.引言

2020年以来,我们国家爆发传染性新冠肺炎重大卫生事件,此次重大的卫生事件影响了全国人民的日常、经济生活。疫情的严重性严重危害人们的健康,后期世界各国不断爆发传染性新冠肺炎疫情。疫情期间,大家最关心的就是疫情数据,每日关注的就是新增数据数量。单一的数据对于人的警告作用非常有限,对于不同地区的情况了解甚少,缺乏全局局势的认知。疫情数据可视化结合了地区图例、疫情数据,生动、形象、具体地展示了数据与自身相关地区的关系,利于人们充分意识到疫情的严重性。

2.系统结构。

图1 系统框架图

2.1 django

Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

2.2 request

requests可以模拟浏览器的请求,快速获取网页的信息。

2.3 re

Python内置正则表达式模块:正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。

2.4 pyechart

pyecharts 是一个用于生成 Echarts 图表的类库。Echart是百度开源的一个数据可视化 JS 库。用 Echarts 生成的图可视化效果非常棒,使用方便快捷。

3.实现代码。

3.1数据获取和过滤

Get_data():获取的是世界各国的疫情数据,用于制作数据地图。

首先通过request获取指定网页地址的信息,将获取的数据文本重新编码(decode);再调用re模块正则化匹配需要的信息文本,红色字体开始匹配获取的就是世界疫情数据,根据你需要获取的数据和网页请求文本进行相对应修改;最后调用json模块把整理好的文件存储为json格式。

 

Get_statics_data():获取地区过去的历史数据,用于制作数据趋势图。

历史数据的获取地址存储get_data()获取的数据里面,Key“statisticsData”对应的就是地址;读取文件,获取地址,通过request再次请求获取数据;将获取的数据制作json格式:key(地区名):value(该地区的历史数据),保存。

 

Data_request.py

import json
import re
import requests
import datetime

today = datetime.date.today().strftime('%Y%m%d')   #20200315

def get_data():
    """
    爬取实时统计数据,保存到data目录下,以当前日期作为文件名,存JSON文件
    """
    
    response = requests.get('https://ncov.dxy.cn/ncovh5/view/pneumonia') #request.get()用于请求目标网站
    print(response.status_code)                                          # 打印状态码
    

    try:
        url_text = response.content.decode()                             
        #print(url_text)
        url_content = re.search(r'window.getListByCountryTypeService2true = (.*?)}]}catch',   #re.search():扫描字符串以查找正则表达式模式产生匹配项的第一个位置 ,然后返回相应的match对象。
                                url_text, re.S)                          #在字符串a中,包含换行符\n,在这种情况下:如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始;
                                                                         #而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。
        texts = url_content.group()                                      
        content = texts.replace('window.getListByCountryTypeService2true = ', '').replace('}catch', '') #去除多余的字符
        json_data = json.loads(content)                                         
        with open( 'data/world/'+ today + '.json', 'w', encoding='UTF-8') as f:
            json.dump(json_data, f, ensure_ascii=False)
    except:
        print('<Response [%s]>' % response.status_code)


def get_statistics_data():
    """
    获取各个省份历史统计数据,保存到data目录下,存JSON文件
    """
    with open('data/world/' + today + '.json', 'r', encoding='UTF-8') as file:
        json_array = json.loads(file.read())

    statistics_data = {}
    for province in json_array:
        response = requests.get(province['statisticsData'])
        try:
            statistics_data[province['provinceName']] = json.loads(response.content.decode())['data']
        except:
            print('<Response [%s]> for url: [%s]' % (response.status_code, province['statisticsData']))

    with open("data/world/statistics_data"+today + ".json", "w", encoding='UTF-8') as f:
        json.dump(statistics_data, f, ensure_ascii=False)

3.2数据可视化

3.2.1疫情地图

Data_in():绘制的是世界疫情地图,中国疫情地图类似,地图类型与数据更换即可,返回的是制作制作成的html文件。

    首先,打开用于制作世界疫情地图的数据,对一些Value进行更换,有一些国家名称与地图上的不匹配,修改为一致;接下提取你想要绘制的数据类型,这里挑选(当前确诊人数、死亡人数、治愈人数);pieces为图例层级颜色,基于 pyechart格式要求保存。调用pyechart模块的Map()类,创建你需要的地图类型(世界地图、国家地图、省级地图)。

set_series_opts()方法:系列配置项,可配置图元样式、文字样式、标签样式、点线样式等;这里设置label_opt = False,世界地图国家较多,显示国家名称过于杂乱。

set_global_opts()方法:全局配置项,可配置标题、动画、坐标轴、图例等;title_opts设置标题,legend_opts设置图例模式、坐标,visualmap_opts设置我们不同数据类型的显示图例、分段显示的颜色。

       调用render()把图例导出为html文件,保存,供网页请求调用。

Data_select.py
import json
import datetime
from pyecharts.charts import Map
from pyecharts import options as opts
import numpy as np
from pyecharts.charts import Line


def data_in():
	# 读原始数据文件
	today = datetime.date.today().strftime('%Y%m%d')   
	datafile = 'data/world/' + today + '.json'
	print(datafile)
	with open(datafile, 'r', encoding='UTF-8') as file:
		json_array = json.loads(file.read())

	#个别数据标签不匹配,要做修改
	world_data = []
	json_array[0]['countryFullName'] = 'United States'
	json_array[2]['countryFullName'] = 'United Kingdom'
	json_array[3]['countryFullName'] = 'Russia'
	#json_array[21]['countryFullName'] = 'Iran'
	json_array[25]['countryFullName'] = 'Bolivia'
	json_array[31]['countryFullName'] = 'Dominican Rep.'
	json_array[53]['countryFullName'] = 'Dem. Rep. Congo'
	json_array[58]['countryFullName'] = 'Sudan'
	json_array[65]['countryFullName'] = 'Uzbekistan'
	json_array[72]['countryFullName'] = 'Central African Rep.'
	json_array[86]['countryFullName'] = 'S. Sudan'
	json_array[95]['countryFullName'] = 'Korea'

	#print(json_array[3]['countryFullName'])
	for province in json_array:
		world_data.append((province['countryFullName'], province['currentConfirmedCount'],  province['deadCount'],  province['curedCount']))
	world_data = sorted(world_data, key=lambda x: x[1], reverse=True)                 #reverse=True,表示降序,反之升序

	# 全球疫情地图
	# 自定义的每一段的范围,以及每一段的特别的样式。
	pieces = [
		{'min': 1000000, 'color': '#540d0d'},
		{'max': 999999, 'min': 100000, 'color': '#9c1414'},
		{'max': 99999, 'min': 10000, 'color': '#d92727'},
		{'max': 9999, 'min': 1000, 'color': '#ed3232'},
		{'max': 999, 'min': 100, 'color': '#f27777'},
		{'max': 99, 'min': 1, 'color': '#f7adad'},
		{'max': 0, 'color': '#f7e4e4'},
	]
	labels = [data[0] for data in world_data]
	counts = [data[1] for data in world_data]
	counts_dead = [data[2] for data in world_data]
	counts_cured = [data[3] for data in world_data]

	#print(labels[0])

	m = Map()
	m.add("当前确诊", [list(z) for z in zip(labels, counts)], 'world')
	m.add("死亡人数", [list(z) for z in zip(labels, counts_dead)], 'world')
	m.add("治愈人数", [list(z) for z in zip(labels, counts_cured)], 'world')

	#系列配置项,可配置图元样式、文字样式、标签样式、点线样式等
	m.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
	#全局配置项,可配置标题、动画、坐标轴、图例等
	m.set_global_opts(title_opts=opts.TitleOpts(title='全球实时确诊数据 '+today,
		                                        subtitle='数据来源:丁香园'),
		              #legend_opts=opts.LegendOpts(is_show=False),
		              legend_opts=opts.LegendOpts(selected_mode='single', orient='vertical', pos_right=0, pos_top=40),
		              visualmap_opts=opts.VisualMapOpts(pieces=pieces,
		                                                is_piecewise=True,   #是否为分段型
		                                                is_show=True))       #是否显示视觉映射配置
	#render()会生成本地 HTML 文件,默认会在当前目录生成 render.html 文件,也可以传入路径参数,如 m.render("mycharts.html")
	path = 'templates/data_show/全球实时确诊数据' + today + '.html'
	m.render(path=path)
	path = 'data_show/全球实时确诊数据' + today + '.html'
	return path

3.2.2趋势图

data_trend():绘制的中国增长趋势图,返回的图例文件存储路径。

       读取static_data文件,制作x轴的标签,从2月1号开始截取,标签为月份-日期;static_data文件保存的是一个国家各省份的历史数据(含各种类型人数),取出需要制作成趋势图的确诊人数,把当天所有省份的确诊人数相加,得到的就是全国新增确证人数;

       新建xy坐标轴,填充xy轴数据,设置图例、标题、曲线类型、颜色等;导出制作好的图例文件保存。

 

 

Data_select_china.py

def data_trend():
	# 读原始数据文件
	today = datetime.date.today().strftime('%Y%m%d') 
	datafile = 'data/china/statistics_data' + today +'.json'
	with open(datafile, 'r', encoding='UTF-8') as file:
	    json_dict = json.loads(file.read())

	# 获取日期列表 取出日期格式 用于x坐标的使用
	dateId = [str(da['dateId'])[4:6] + '-' + str(da['dateId'])[6:8] for da in json_dict['湖北省'] if
		  da['dateId'] >= 20200201]

	# 分析各省份2月1日至今的新增确诊数据:'confirmedIncr'
	statistics__data = {}
	for province in json_dict:
		statistics__data[province] = []
		for da in json_dict[province]:
			if da['dateId'] >= 20200201:
				statistics__data[province].append(da['confirmedIncr'])
	    #若当天该省数据没有更新,则默认为0
		if(len(statistics__data[province])!=len(dateId)):
			statistics__data[province].append(0)


	# 全国新增趋势 
	all_statis = np.array([0] * len(dateId))

	for province in statistics__data:
	    #print(province)
	    all_statis = all_statis + np.array(statistics__data[province])

	all_statis = all_statis.tolist()

	line = Line()
	line.add_xaxis(dateId)
	line.add_yaxis("全国新增确诊病例" + today,   #图例
		        all_statis,       #数据
		        is_smooth=True,   #是否平滑曲线
		       linestyle_opts=opts.LineStyleOpts(width=4, color='#B44038'),#线样式配置项
		       itemstyle_opts=opts.ItemStyleOpts(color='#B44038',          #图元样式配置项
		                                         border_color="#B44038",   #颜色
		                                         border_width=10))         #图元的大小

	line.set_global_opts(title_opts=opts.TitleOpts(title="新增确诊病例", subtitle='数据来源:丁香园'),
		             yaxis_opts=opts.AxisOpts(max_=16000, min_=1, type_="log",    #坐标轴配置项
		                                      splitline_opts=opts.SplitLineOpts(is_show=True),#分割线配置项
		                                      axisline_opts=opts.AxisLineOpts(is_show=True)))#坐标轴刻度线配置项
	path = 'templates/data_show/trend/全国新增确诊趋势图' + today + '.html'
	line.render(path=path)
	path = 'data_show/trend/全国新增确诊趋势图' + today + '.html'
	return path

3.3部署django

3.3.1创建项目、app

安装django,命令:pip install Django==1.11.4

创建项目:django-admin startproject myproject

在项目目录下创建一个软件app :django-admin startapp get_data

setting.py文件下添加软件名称,如图2

图2

在项目目录下,创建文件夹templates用于存储html文件

在setting.py文件下添加文件夹,如图3

图3

3.3.2编写urls.py

urls.py

浏览器输入的地址将会根据正则表达式,在这里得到匹配,匹配成功调用相对应的views方法。

from django.conf.urls import url
from django.contrib import admin
from get_data import views

urlpatterns = [
    url(r'^map/$', views.test, name='map'),
    url(r'^trend$', views.trend, name='trend'),
    url(r'^admin/', admin.site.urls),
]

3.3.3编写views.py

以下两个类,分别对应两个网页的响应措施

Test()、trend():首先根据当日的日期拼接出文件路径,判断服务器端是否已经更新文件;有则直接返回html文件给前端调用;无则服务器马上去获取新数据,调用上面个将的数据获取、数据可视化方法。

from django.shortcuts import render
import datetime
import os
# Create your views here.
from .data_request import get_data, get_statistics_data
from .data_request_china import get_data_china, get_datatrend_china
from .data_select import data_in
from .data_select_china import data_in_china, data_trend


today = datetime.date.today().strftime('%Y%m%d') 
print(today)
def test(request):
    
    #获取世界确诊人数、并绘制地图
    file_path = 'templates/data_show/全球实时确诊数据' + today + '.html'
    flag = os.path.exists(file_path)
    if flag:
    	print(1)
    	path = 'data_show/全球实时确诊数据' + today + '.html'
    else:
    	get_data()
    	path = data_in()
    
    #获取全国确诊人数、并绘制地图
    file_path = 'templates/data_show/全国实时确诊数据' + today + '.html'
    flag = os.path.exists(file_path)
    if flag:
    	print(2)
    	path_china = 'data_show/全国实时确诊数据' + today + '.html'
    else:
    	get_data_china()
    	path_china = data_in_china()

    

    path = str(path)
    path_china = str(path_china)
    print(path)
    return render(request, 'test_data_show.html',{'path': path, 'path_china':path_china})


def trend(request):
    #获取全国静态数据,绘制趋势图
    file_path = 'templates/data_show/trend/全国新增确诊趋势图' + today + '.html'
    flag = os.path.exists(file_path)
    if flag:
    	path = 'data_show/trend/全国新增确诊趋势图' + today + '.html'
    	print(3)
    else:	
    	get_datatrend_china()
    	path = data_trend()
    return render(request, 'test_data_show_trend.html',{'path': path})

3.3.4编写html文件

编写一个模板html文件,模板文件定义了html版面格式一致;

Html里面传递的参数,就是通过在上面一个views方法调用传递进来的。

 

Base.html

{% load static %}<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>{% block title %}python小项目{% endblock %}</title>
		<link href="https://fonts.googleapis.com/css?family=Peralta" rel="stylesheet">
		<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
		<link rel="stylesheet" href="{% static 'css/app.css' %}">
		{% block stylesheet %}{% endblock %} 
	</head>
	<body>
		{% block body %}
			<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
				<div class="container">
					<a class="navbar-brand" href="#">python小项目</a>
					<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mainMenu" aria-controls="mainMenu"aria-expanded="false" aria-label="Toggle navigation">
						<span class="navbar-toggler-icon"></span>
					</button>
				</div>
			</nav>

			<div class="container">
				<ol class="breadcrumb my-4">
					{% block breadcrumb %}
					{% endblock %}
				</ol>
				{% block content %}
				{% endblock %}
			</div>
		{% endblock body %}
		<script src="{% static 'js/jquery-3.2.1.min.js' %}"></script>
		<script src="{% static 'js/popper.min.js' %}"></script>
		<script src="{% static 'js/bootstrap.min.js' %}"></script>
	</body>
</html>

Test_data_show.html

此html返回的是疫情地图画面

返回的就是趋势图例画面

{% extends 'base.html' %}

{% block title %}
	疫情数据可视化
{% endblock %}

{% block breadcrumb %}
	<li ><a href="{% url 'map' %}">疫情地图</a></li>|	
	<li><a href="{% url 'trend' %}">疫情趋势</a></li>	
{% endblock %}

{% block content %}


      <div id="map" class="container tab-pane active"><br>
	<div class="container">
	  <br>
	  <!-- Nav pills -->
	  <ul class="nav nav-pills" role="tablist">
	    <li class="nav-item">
	      <a class="nav-link active" data-toggle="pill" href="#world">全球疫情</a>
	    </li>
	    <li class="nav-item">
	      <a class="nav-link" data-toggle="pill" href="#china">全国疫情</a>
	    </li>
	  </ul>
	
	  <!-- Tab panes -->
	  <div class="tab-content">
	    <div id="world" class="container tab-pane active"><br>
	      {% include path %}
	    </div>
	    <div id="china" class="container tab-pane fade"><br>
	      {% include path_china %}
	    
	    </div>
	  </div>
	</div>
      </div>

      <div id="trend" class="container tab-pane active"><br>

      </div>
{% endblock %}


Data_show_trend.html

{% extends 'base.html' %}

{% block title %}
	疫情监测
{% endblock %}

{% block breadcrumb %}
	<li><a href="{% url 'map' %}">疫情地图</a></li> |	
	<li><a href="{% url 'trend' %}">疫情趋势</a></li>
	
{% endblock %}

{% block content %}
      <div id="map" class="container tab-pane active"><br>
	<div class="container">
	  <br>
	  <!-- Nav pills -->
	  <ul class="nav nav-pills " role="tablist">
	    <li class="nav-item">
	      <a class="nav-link active" data-toggle="pill" href="#china">中国</a>
	    </li>
	    <li class="nav-item">
	      <a class="nav-link" data-toggle="pill" href="#usa">美国</a>
	    </li>
	  </ul>
	
	  <!-- Tab panes -->
	  <div class="tab-content">
	    <div id="china" class="container tab-pane active"><br>
	      {% include path %}
	    </div>

	    <div id="usa" class="container tab-pane fade"><br>
	   
	  
	    </div>
	  </div>
	</div>
      </div>

      <div id="trend" class="container tab-pane active"><br>

      </div>
{% endblock %}

3.3.5包含一些js、css文件

文件的存储路径,在项目目录下,创建static文件夹

并在setting.py文件末尾添加图4所示代码,以便可以找到文件

图4

用途:html渲染需要

如图5、图6所示

图5

图6

4.实验。

运行程序,在文件目录下输入以下命令,如图7

图7

在浏览器输入地址,如图8

图8

 

4.1疫情地图

世界疫情地图,可点击切换全国疫情地图,右边可切换不同类型的疫情数据地图如图9、图10、图11

图9

图10

图11

 

 

当鼠标移动到地图图例时将会显示该类型的数据大小,如北京,在7月6日的确证人数有312人,如图12

图12

 

 

4.2疫情趋势图

可以输入如下图网址,或者点击疫情趋势,可以查看到这一段时间疫情走势,时间是从2月1号开始统计,当下7月6号新增确诊病例稳定控制在10-100区间,如图13、14

图13

图14

5.总结和展望。

了解了django的基本使用,快速实现前后端的部署;在django中app的创建与调用;通过学习和了解python不同的丰富模块,更容易理解python编程语言在实践之中的运用。对对于学习物联网,后端的数据库也是很重要的一环,终端收集的数据需要存储在后端的数据库,用户显示与控制终端又是对数据库的控制与操作。学会运用基于python的django框架,能提供很大的便利,快速打通物联网的一套通信流程。

运用python request模块、re模块、json模块,对于获取信息提升了很大的便利,这些模块在文件处理方面体现高效;pyechart是数据客观展示的途径,了解和学习使用该模块对于生活当中也是非常有必要,作用如word、ppt软件一般。

参考文献:

[1]《django入门与实践》Django ⼊⻔与实践教程是由咱们公众号「Python之禅」 发起的A Complete Beginner's Guide to Django翻译计划;

  • 6
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值