简单的疫情可视化Django应用


写在前面

以前本站的疫情可视化只是一个简单的html页面,使用crontab定时任务更新数据,每天早上八点更新一次,这种方式存在诸多弊端,主要是无法拓展更多功能,但也有明显的好处,占用资源很少,不会影响服务器性能,也不会对网站安全造成影响,添加或移除该功能的时候也简单。总之,使用静态页面更像是在混吃等死。三天前我在查找资料的时候翻到了pyecharts中文文档,文档中介绍了如何将pyecharts整合到web框架,而且很贴心的给出前后端分离的教程,所以,我就依葫芦画瓢的搞了一通,替换掉了原本的静态页面。

准备工作

#创建虚拟环境
py -3 -m venv myproject_env #Windows
Virtualenv -p /usr/bin/python3 myproject_env #Linux
#启动虚拟环境
\myproject_env\Scripts\activate #Windows
source myproject_env/bin/activate #Linux
#安装Django
pip install Django==3.1.3
#创建项目
django-admin startproject myproject

创建APP

#切换目录
cd myproject
#创建app
python manage.py startapp echarts

创建完成后修改myproject/settings.py,将echarts添加到INSTALLED_APPS中:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'echarts',
    'rest_framework',
]

其他依赖

djangorestframework==3.12.2
lxml==4.6.2
openpyxl==3.0.5
pyecharts==1.9.0
requests==2.25.1

更改主路由

myproject/urls.py

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('echarts/',include('echarts.urls',namespace = 'echarts')),
]

更改app视图

echarts/views.py

#网页向导与重定向
from django.shortcuts import render,redirect
#json字典
import json
#网页响应
from django.http import HttpResponse
#前后端分离需要使用该类视图
from rest_framework.views import APIView
#爬虫代码,抓取数据
from . import data_get
#绘图
from . import data_map
#将文本中的数据渲染为字典
import ast
# Create your views here.
#以下三个函数来自pyecharts文档,主要作用是渲染绘图数据
#json_error未使用
def response_as_json(data):
    json_str = json.dumps(data)
    response = HttpResponse(
        json_str,
        content_type = 'application/json',
    )
    response["Access-Control-Allow-Origin"] = '*'
    return response

def json_response(data,code=200):
    data = {
        "code":code,
        "msg":"success",
        "data":data,
    }
    return response_as_json(data)

def json_error(error_string = 'error',code=500,**kwargs):
    data = {
        "code":code,
        "msg":error_string,
        "data":{},
    }
    data.update(kwargs)
    return response_as_json(data)

#获取绘图数据,定义为全局变量
def plot_map(data):
    global ct_map, cs_map,fi_map,gt_map,gs_map,fte_map
    #获取数据表
    wb_china = data_get.china_total_data(data)
    wb_global = data_get.global_total_data(data)
    #获取地图对象
    ct_map, cs_map = data_map.china_total_map(wb_china)
    fi_map = data_map.china_daily_map(wb_china)
    gt_map, gs_map = data_map.global_total_map(wb_global)
    fte_map = data_map.foreign_daily_map(wb_global)

JsonResponse = json_response
JsonError = json_error
#不需要更新时从json文本中读取数据
f = open('./echarts/covid_19.json','r',encoding = 'utf-8')
#渲染为字典
data = ast.literal_eval(f.read())
#释放文件
f.close()
#得到绘图对象
plot_map(data)

#点击更新时从百度获取最新数据,并在外部更新json文本,同时重新获取绘图对象
def update(request):
    data = data_get.init()
    plot_map(data)
    #重定向页面
    return redirect('echarts:covid_19')

#绘制中国累计确诊地图
class CtView(APIView):
    def get(self,request,*args,**kwargs):
        global ct_map
        return JsonResponse(json.loads(ct_map))

#绘制中国现有确诊地图
class CsView(APIView):
    def get(self,request,*args,**kwargs):
        global cs_map
        return JsonResponse(json.loads(cs_map))

#绘制中国疫情走势地图
class CdView(APIView):
    def get(self,request,*args,**kwargs):
        global fi_map
        return JsonResponse(json.loads(fi_map))

#绘制全球累计确诊地图
class GtView(APIView):
    global gt_map
    def get(self,request,*args,**kwargs):
        return JsonResponse(json.loads(gt_map))

#绘制全球现有确诊地图
class GsView(APIView):
    def get(self,request,*args,**kwargs):
        global gs_map
        return JsonResponse(json.loads(gs_map))

#绘制境外疫情走势(不含中国)
class GdView(APIView):
    def get(self,request,*args,**kwargs):
        global gs_map
        return JsonResponse(json.loads(fte_map))

#主页面
class IndexView(APIView):
    def get(self,request,*args,**kwargs):
        return render(request,'echarts/covid_19.html')

爬虫代码

在echarts目录下新建data_get.py:

import json
import openpyxl
import requests
from lxml import etree

def init():
    headers = {
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
            }
    url = 'https://voice.baidu.com/act/newpneumonia/newpneumonia/'
    response = requests.get(url = url,headers = headers)
    tree = etree.HTML(response.text)
    dict1 = tree.xpath('//script[@id="captain-config"]/text()')
    dict2 = json.loads(dict1[0])
    f = open('./echarts/covid_19.json','w',encoding = 'utf-8')
    f.write(str(dict2))
    f.close()
    return dict2

def china_total_data(data):
    wb = openpyxl.Workbook()
    ws_china = wb.active
    ws_china.title = '中国省份疫情数据'
    ws_china.append(['省/直辖市/自治区/行政区','现有确诊','累计确诊','累计治愈',
                     '累计死亡','现有确诊增量','累计确诊增量',
                     '累计治愈增量','累计死亡增量'])
    china = data['component'][0]['caseList']
    for province in china:
        ws_china.append([
            province['area'],
            province['curConfirm'],
            province['confirmed'],
            province['crued'],
            province['died'],
            province['curConfirmRelative'],
            province['confirmedRelative'],
            province['curedRelative'],
            province['diedRelative']
            ])
    ws_city = wb.create_sheet('中国城市疫情数据')
    ws_city.append([
        '城市','现有确诊','累计确诊','累计治愈','累计死亡','累计确诊增量'
        ])
    for province in china:
        for city in province['subList']:
            if 'curConfirm' not in city:
                city['curConfirm'] = '0'
            if city['crued'] == '':
                city['crued'] = '0'
            if city['died'] == '':
                city['died'] = '0'
            if 'confirmedRelative' not in city:
                city['confirmedRelative'] = '0'
            ws_city.append([
                city['city'],'0',city['confirmed'],
                city['crued'],city['died'],city['confirmedRelative']
                ])
    time_domestic = data['component'][0]['mapLastUpdatedTime']
    ws_time = wb.create_sheet('中国疫情数据更新时间')
    ws_time.column_dimensions['A'].width = 22
    ws_time.append(['中国疫情数据更新时间'])
    ws_time.append([time_domestic])
    return china_daily_data(data,wb)

def global_total_data(data):
    wb = openpyxl.Workbook()
    ws_global = wb.active
    ws_global.title = '全球各国疫情数据'

    countries = data['component'][0]['caseOutsideList']
    ws_global.append(['国家','现有确诊','累计确诊','累计治愈','累计死亡','累计确诊增量'])
    for country in countries:
        ws_global.append([
            country['area'],
            country['curConfirm'],
            country['confirmed'],
            country['crued'],
            country['died'],
            country['confirmedRelative']
            ])
    continent = data['component'][0]['globalList']
    for area in continent:
        ws_foreign = wb.create_sheet(area['area'] + '疫情数据')
        ws_foreign.append(['国家','现有确诊','累计确诊','累计治愈','累计死亡','累计确诊增量'])
        for country in area['subList']:
            ws_foreign.append([
                country['country'],
                country['curConfirm'],
                country['confirmed'],
                country['crued'],
                country['died'],
                country['confirmedRelative']
                ])
    ws1,ws2 = wb['全球各国疫情数据'],wb['亚洲疫情数据']
    original_data = data['component'][0]['summaryDataIn']
    add_china_data = ['中国',
                      original_data['curConfirm'],
                      original_data['confirmed'],
                      original_data['cured'],
                      original_data['died'],
                      original_data['confirmedRelative']
                      ]
    ws1.append(add_china_data)
    ws2.append(add_china_data)
    time_foreign = data['component'][0]['foreignLastUpdatedTime']
    ws_time = wb.create_sheet('全球疫情数据更新时间')
    ws_time.column_dimensions['A'].width = 22
    ws_time.append(['全球疫情数据更新时间'])
    ws_time.append([time_foreign])

    return foreign_daily_data(data,wb)

def china_daily_data(data,wb):
    ccd_dict = data['component'][0]['trend']
    update_date = ccd_dict['updateDate']
    china_confirmed = ccd_dict['list'][0]['data']
    china_crued = ccd_dict['list'][2]['data']
    china_died = ccd_dict['list'][3]['data']
    china_surplus = []
    for i in range(len(update_date)):
        surplus = china_confirmed[i] - china_crued[i] - china_died[i]
        china_surplus.append(surplus)

    ws_china_surplus = wb.create_sheet('中国每日现有确诊数据')
    ws_china_surplus.append(['日期','数据'])
    for data in zip(update_date,china_surplus):
        ws_china_surplus.append(data)

    ws_china_confirmed = wb.create_sheet('中国每日累计确诊数据')
    ws_china_confirmed.append(['日期','数据'])
    for data in zip(update_date,china_confirmed):
        ws_china_confirmed.append(data)

    ws_china_crued = wb.create_sheet('中国每日累计治愈数据')
    ws_china_crued.append(['日期','数据'])
    for data in zip(update_date,china_crued):
        ws_china_crued.append(data)

    ws_china_died = wb.create_sheet('中国每日累计死亡数据')
    ws_china_died.append(['日期','数据'])
    for data in zip(update_date,china_died):
        ws_china_died.append(data)

    return wb

def foreign_daily_data(data,wb):
    te_dict = data['component'][0]['allForeignTrend']
    update_date = te_dict['updateDate']
    foreign_confirmed = te_dict['list'][0]['data']
    foreign_crued = te_dict['list'][1]['data']
    foreign_died = te_dict['list'][2]['data']
    foreign_surplus = []
    for i in range(len(update_date)):
        surplus = foreign_confirmed[i] - foreign_crued[i] - foreign_died[i]
        foreign_surplus.append(surplus)

    ws_foreign_surplus = wb.create_sheet('境外每日现有确诊数据')
    ws_foreign_surplus.append(['日期','数据'])
    for data in zip(update_date,foreign_surplus):
        ws_foreign_surplus.append(data)

    ws_foreign_confirmed = wb.create_sheet('境外每日累计确诊数据')
    ws_foreign_confirmed.append(['日期','数据'])
    for data in zip(update_date,foreign_confirmed):
        ws_foreign_confirmed.append(data)

    ws_foreign_crued = wb.create_sheet('境外每日累计治愈数据')
    ws_foreign_crued.append(['日期','数据'])
    for data in zip(update_date,foreign_crued):
        ws_foreign_crued.append(data)

    ws_foreign_died = wb.create_sheet('境外每日累计死亡数据')
    ws_foreign_died.append(['日期','数据'])
    for data in zip(update_date,foreign_died):
        ws_foreign_died.append(data)

    return wb

if __name__ == '__main__':
    init()

新建data_map.py:

import openpyxl
import ast
from pyecharts.charts import Map,Page,Line
from pyecharts.globals import CurrentConfig
import pyecharts.options as opts
import pyecharts.globals
import warnings
import json
warnings.filterwarnings("ignore")
pyecharts.globals._WarningControl.ShowWarning = False
CurrentConfig.ONLINE_HOST = "./static/js/"

def china_total_map(wb):
    ws_time = wb['中国疫情数据更新时间']
    ws_data = wb['中国省份疫情数据']
    ws_data.delete_rows(1)
    province = []
    curconfirm = []
    surplus = []
    for data in ws_data.values:
        province.append(data[0])
        surplus.append(data[1])
        curconfirm.append(data[2])    
    time_china = ws_time['A2'].value

    pieces = [
        {'max':0,'min':0,'label':'0','color':'#FFFFFF'},
        {'max':9,'min':1,'label':'1-9','color':'#FFE5DB'},
        {'max':99,'min':10,'label':'10-99','color':'#FF9985'},
        {'max':999,'min':100,'label':'100-999','color':'#F57567'},
        {'max':9999,'min':1000,'label':'1000-9999','color':'#E64546'},
        {'max':99999,'min':10000,'label':'>=10000','color':'#B80909'},
        ]
    ct_map = (
        Map()
        .add(series_name = '累计确诊人数',data_pair = [list(z) for z in zip(province,curconfirm)],maptype = "china",is_map_symbol_show = False)
        .set_global_opts(
            title_opts = opts.TitleOpts(
                title = "中国疫情数据(累计确诊)",
                subtitle = '数据更新至:' + time_china + '\n\n来源:百度疫情实时大数据报告'),
            visualmap_opts = opts.VisualMapOpts(max_ = 300,is_piecewise = True,pieces = pieces)
            )
            .dump_options_with_quotes()
        )
    cs_map = (
        Map()
        .add(series_name = '现有确诊人数',data_pair = [list(z) for z in zip(province,surplus)],maptype = "china",is_map_symbol_show = False)
        .set_global_opts(
            title_opts = opts.TitleOpts(
                title = "中国疫情数据(现有确诊)",
                subtitle = '数据更新至:' + time_china + '\n\n来源:百度疫情实时大数据报告'),
            visualmap_opts = opts.VisualMapOpts(max_ = 300,is_piecewise = True,pieces = pieces)
            )
        .dump_options_with_quotes()
        )
    return ct_map,cs_map

def global_total_map(wb):
    ws_time = wb['全球疫情数据更新时间']
    ws_data = wb['全球各国疫情数据']
    ws_data.delete_rows(1)
    country = []
    surplus = []
    curconfirm = []
    for data in ws_data.values:
        country.append(data[0])
        surplus.append(data[1])
        curconfirm.append(data[2])    
    time_global = ws_time['A2'].value

    pieces = [
        {'max':0,'min':0,'label':'0','color':'#FFFFFF'},
        {'max':49,'min':1,'label':'1-49','color':'#FFE5DB'},
        {'max':99,'min':50,'label':'50-99','color':'#FFC4B3'},
        {'max':999,'min':100,'label':'100-999','color':'#FF9985'},
        {'max':9999,'min':1000,'label':'1000-9999','color':'#F57567'},
        {'max':99999,'min':10000,'label':'10000-99999','color':'#E64546'},
        {'max':999999,'min':100000,'label':'100000-999999','color':'#B80909'},
        {'max':9999999,'min':1000000,'label':'1000000-9999999','color':'#BA0808'},
        {'max':99999999,'min':10000000,'label':'>=10000000','color':'#F00000'}
        ]
    name_map = {
        "Somalia": "索马里",
        "Liechtenstein": "列支敦士登",
        "Morocco": "摩洛哥",
        "W. Sahara": "西撒哈拉",
        "Serbia": "塞尔维亚",
        "Afghanistan": "阿富汗",
        "Angola": "安哥拉",
        "Albania": "阿尔巴尼亚",
        "Andorra": "安道尔共和国",
        "United Arab Emirates": "阿拉伯联合酋长国",
        "Argentina": "阿根廷",
        "Armenia": "亚美尼亚",
        "Australia": "澳大利亚",
        "Austria": "奥地利",
        "Azerbaijan": "阿塞拜疆",
        "Burundi": "布隆迪",
        "Belgium": "比利时",
        "Benin": "贝宁",
        "Burkina Faso": "布基纳法索",
        "Bangladesh": "孟加拉国",
        "Bulgaria": "保加利亚",
        "Bahrain": "巴林",
        "Bahamas": "巴哈马",
        "Bosnia and Herz.": "波斯尼亚和黑塞哥维那",
        "Belarus": "白俄罗斯",
        "Belize": "伯利兹",
        "Bermuda": "百慕大",
        "Bolivia": "玻利维亚",
        "Brazil": "巴西",
        "Barbados": "巴巴多斯",
        "Brunei": "文莱",
        "Bhutan": "不丹",
        "Botswana": "博茨瓦纳",
        "Central African Rep.": "中非共和国",
        "Canada": "加拿大",
        "Switzerland": "瑞士",
        "Chile": "智利",
        "China": "中国",
        "Côte d'Ivoire": "科特迪瓦",
        "Cameroon": "喀麦隆",
        "Dem. Rep. Congo": "刚果(布)",
        "Congo": "刚果(金)",
        "Colombia": "哥伦比亚",
        "Cape Verde": "佛得角",
        "Costa Rica": "哥斯达黎加",
        "Cuba": "古巴",
        "N. Cyprus": "北塞浦路斯",
        "Cyprus": "塞浦路斯",
        "Czech Rep.": "捷克",
        "Germany": "德国",
        "Djibouti": "吉布提",
        "Denmark": "丹麦",
        "Dominican Rep.": "多米尼加",
        "Algeria": "阿尔及利亚",
        "Ecuador": "厄瓜多尔",
        "Egypt": "埃及",
        "Eritrea": "厄立特里亚",
        "Spain": "西班牙",
        "Estonia": "爱沙尼亚",
        "Ethiopia": "埃塞俄比亚",
        "Finland": "芬兰",
        "Fiji": "斐济",
        "France": "法国",
        "Gabon": "加蓬",
        "United Kingdom": "英国",
        "Georgia": "格鲁吉亚",
        "Ghana": "加纳",
        "Guinea": "几内亚",
        "Gambia": "冈比亚",
        "Guinea-Bissau": "几内亚比绍",
        "Eq. Guinea": "赤道几内亚",
        "Greece": "希腊",
        "Grenada": "格林纳达",
        "Greenland": "格陵兰岛",
        "Guatemala": "危地马拉",
        "Guam": "关岛",
        "Guyana": "圭亚那合作共和国",
        "Honduras": "洪都拉斯",
        "Croatia": "克罗地亚",
        "Haiti": "海地",
        "Hungary": "匈牙利",
        "Indonesia": "印度尼西亚",
        "India": "印度",
        "Br. Indian Ocean Ter.": "英属印度洋领土",
        "Ireland": "爱尔兰",
        "Iran": "伊朗",
        "Iraq": "伊拉克",
        "Iceland": "冰岛",
        "Israel": "以色列",
        "Italy": "意大利",
        "Jamaica": "牙买加",
        "Jordan": "约旦",
        "Japan": "日本",
        "Siachen Glacier": "锡亚琴冰川",
        "Kazakhstan": "哈萨克斯坦",
        "Kenya": "肯尼亚",
        "Kyrgyzstan": "吉尔吉斯斯坦",
        "Cambodia": "柬埔寨",
        "Korea": "韩国",
        "Kuwait": "科威特",
        "Lao PDR": "老挝",
        "Lebanon": "黎巴嫩",
        "Liberia": "利比里亚",
        "Libya": "利比亚",
        "Sri Lanka": "斯里兰卡",
        "Lesotho": "莱索托",
        "Lithuania": "立陶宛",
        "Luxembourg": "卢森堡",
        "Latvia": "拉脱维亚",
        "Moldova": "摩尔多瓦",
        "Madagascar": "马达加斯加",
        "Mexico": "墨西哥",
        "Macedonia": "马其顿",
        "Mali": "马里",
        "Malta": "马耳他",
        "Myanmar": "缅甸",
        "Montenegro": "黑山",
        "Mongolia": "蒙古国",
        "Mozambique": "莫桑比克",
        "Mauritania": "毛里塔尼亚",
        "Mauritius": "毛里求斯",
        "Malawi": "马拉维",
        "Malaysia": "马来西亚",
        "Namibia": "纳米比亚",
        "New Caledonia": "新喀里多尼亚",
        "Niger": "尼日尔",
        "Nigeria": "尼日利亚",
        "Nicaragua": "尼加拉瓜",
        "Netherlands": "荷兰",
        "Norway": "挪威",
        "Nepal": "尼泊尔",
        "New Zealand": "新西兰",
        "Oman": "阿曼",
        "Pakistan": "巴基斯坦",
        "Panama": "巴拿马",
        "Peru": "秘鲁",
        "Philippines": "菲律宾",
        "Papua New Guinea": "巴布亚新几内亚",
        "Poland": "波兰",
        "Puerto Rico": "波多黎各",
        "Dem. Rep. Korea": "朝鲜",
        "Portugal": "葡萄牙",
        "Paraguay": "巴拉圭",
        "Palestine": "巴勒斯坦",
        "Qatar": "卡塔尔",
        "Romania": "罗马尼亚",
        "Russia": "俄罗斯",
        "Rwanda": "卢旺达",
        "Saudi Arabia": "沙特阿拉伯",
        "Sudan": "苏丹",
        "S. Sudan": "南苏丹",
        "Senegal": "塞内加尔",
        "Singapore": "新加坡",
        "Solomon Is.": "所罗门群岛",
        "Sierra Leone": "塞拉利昂",
        "El Salvador": "萨尔瓦多",
        "Suriname": "苏里南",
        "Slovakia": "斯洛伐克",
        "Slovenia": "斯洛文尼亚",
        "Sweden": "瑞典",
        "Swaziland": "斯威士兰",
        "Seychelles": "塞舌尔",
        "Syria": "叙利亚",
        "Chad": "乍得",
        "Togo": "多哥",
        "Thailand": "泰国",
        "Tajikistan": "塔吉克斯坦",
        "Turkmenistan": "土库曼斯坦",
        "Timor-Leste": "东帝汶",
        "Tonga": "汤加",
        "Trinidad and Tobago": "特立尼达和多巴哥",
        "Tunisia": "突尼斯",
        "Turkey": "土耳其",
        "Tanzania": "坦桑尼亚",
        "Uganda": "乌干达",
        "Ukraine": "乌克兰",
        "Uruguay": "乌拉圭",
        "United States": "美国",
        "Uzbekistan": "乌兹别克斯坦",
        "Venezuela": "委内瑞拉",
        "Vietnam": "越南",
        "Vanuatu": "瓦努阿图",
        "Yemen": "也门",
        "South Africa": "南非",
        "Zambia": "赞比亚",
        "Zimbabwe": "津巴布韦",
        "Aland": "奥兰群岛",
        "American Samoa": "美属萨摩亚",
        "Fr. S. Antarctic Lands": "南极洲",
        "Antigua and Barb.": "安提瓜和巴布达",
        "Comoros": "科摩罗",
        "Curaçao": "库拉索岛",
        "Cayman Is.": "开曼群岛",
        "Dominica": "多米尼加",
        "Falkland Is.": "福克兰群岛马尔维纳斯",
        "Faeroe Is.": "法罗群岛",
        "Micronesia": "密克罗尼西亚",
        "Heard I. and McDonald Is.": "赫德岛和麦克唐纳群岛",
        "Isle of Man": "曼岛",
        "Jersey": "泽西岛",
        "Kiribati": "基里巴斯",
        "Saint Lucia": "圣卢西亚",
        "N. Mariana Is.": "北马里亚纳群岛",
        "Montserrat": "蒙特塞拉特",
        "Niue": "纽埃",
        "Palau": "帕劳",
        "Fr. Polynesia": "法属波利尼西亚",
        "S. Geo. and S. Sandw. Is.": "南乔治亚岛和南桑威奇群岛",
        "Saint Helena": "圣赫勒拿",
        "St. Pierre and Miquelon": "圣皮埃尔和密克隆群岛",
        "São Tomé and Principe": "圣多美和普林西比",
        "Turks and Caicos Is.": "特克斯和凯科斯群岛",
        "St. Vin. and Gren.": "圣文森特和格林纳丁斯",
        "U.S. Virgin Is.": "美属维尔京群岛",
        "Samoa": "萨摩亚"
        }
    gt_map = (
        Map()
        .add(series_name = '累计确诊人数',data_pair = [list(z) for z in zip(country,curconfirm)],maptype = "world",name_map = name_map,is_map_symbol_show = False)
        .set_series_opts(label_opts = opts.LabelOpts(is_show = False))
        .set_global_opts(
            title_opts = opts.TitleOpts(
                title = "全球疫情数据(累计确诊)",
                subtitle = '数据更新至:' + time_global + '\n\n来源:百度疫情实时大数据报告'),
            visualmap_opts = opts.VisualMapOpts(max_ = 300,is_piecewise = True,pieces = pieces)
            )
        .dump_options_with_quotes()
        )
    gs_map = (
        Map()
        .add(series_name = '现有确诊人数',data_pair = [list(z) for z in zip(country,surplus)],maptype = "world",name_map = name_map,is_map_symbol_show = False)
        .set_series_opts(label_opts = opts.LabelOpts(is_show = False))
        .set_global_opts(
            title_opts = opts.TitleOpts(
                title = "全球疫情数据(现有确诊)",
                subtitle = '数据更新至:' + time_global + '\n\n来源:百度疫情实时大数据报告'),
            visualmap_opts = opts.VisualMapOpts(max_ = 300,is_piecewise = True,pieces = pieces)
            )
        .dump_options_with_quotes()
        )
    return gt_map,gs_map

def china_daily_map(wb):
    ws_china_confirmed = wb['中国每日累计确诊数据']
    ws_china_surplus = wb['中国每日现有确诊数据']
    ws_china_crued = wb['中国每日累计治愈数据']
    ws_china_died = wb['中国每日累计死亡数据']

    ws_china_confirmed.delete_rows(1)
    ws_china_surplus.delete_rows(1)
    ws_china_crued.delete_rows(1)
    ws_china_died.delete_rows(1)

    x_date = []
    y_china_confirmed = []
    y_china_surplus = []
    y_china_crued = []
    y_china_died = []

    for china_confirmed in ws_china_confirmed.values:
        y_china_confirmed.append(china_confirmed[1])
    for china_surplus in ws_china_surplus.values:
        y_china_surplus.append(china_surplus[1])
    for china_crued in ws_china_crued.values:
        x_date.append(china_crued[0])
        y_china_crued.append(china_crued[1])
    for china_died in ws_china_died.values:
        y_china_died.append(china_died[1])

    fi_map = (
        Line(init_opts = opts.InitOpts(height = '420px'))
        .add_xaxis(xaxis_data = x_date)
        .add_yaxis(
        series_name = '中国累计确诊人数',
        y_axis = y_china_confirmed,
        label_opts = opts.LabelOpts(is_show = False),
        )
        .add_xaxis(xaxis_data = x_date)
        .add_yaxis(
        series_name = '中国现有确诊人数',
        y_axis = y_china_surplus,
        label_opts = opts.LabelOpts(is_show = False),
        )
        .add_yaxis(
        series_name = '中国累计治愈人数',
        y_axis = y_china_crued,
        label_opts = opts.LabelOpts(is_show = False),
        )
        .add_yaxis(
        series_name = '中国累计死亡人数',
        y_axis = y_china_died,
        label_opts = opts.LabelOpts(is_show = False),
        )
        .set_global_opts(
            title_opts = opts.TitleOpts(title = '中国COVID-19每日数据走势'),
            legend_opts = opts.LegendOpts(pos_bottom = "bottom",orient = 'horizontal'),
            tooltip_opts = opts.TooltipOpts(trigger = 'axis'),
            yaxis_opts = opts.AxisOpts(
                type_ = 'value',
                axistick_opts = opts.AxisTickOpts(is_show = True),
                splitline_opts= opts.SplitLineOpts(is_show = True),
            ),
            xaxis_opts = opts.AxisOpts(type_ = 'category',boundary_gap=False),
        )
        .dump_options_with_quotes()
    )
    return fi_map

def foreign_daily_map(wb):
    ws_foreign_confirmed = wb['境外每日累计确诊数据']
    ws_foreign_surplus = wb['境外每日现有确诊数据']
    ws_foreign_crued = wb['境外每日累计治愈数据']
    ws_foreign_died = wb['境外每日累计死亡数据']

    ws_foreign_confirmed.delete_rows(1)
    ws_foreign_surplus.delete_rows(1)
    ws_foreign_crued.delete_rows(1)
    ws_foreign_died.delete_rows(1)

    x_date = []                # 日期
    y_foreign_confirmed = []   # 累计确诊
    y_foreign_surplus = []
    y_foreign_crued = []       # 累计治愈
    y_foreign_died = []        # 累计死亡

    for foreign_confirmed in ws_foreign_confirmed.values:
        y_foreign_confirmed.append(foreign_confirmed[1])
    for foreign_surplus in ws_foreign_surplus.values:
        y_foreign_surplus.append(foreign_surplus[1])
    for foreign_crued in ws_foreign_crued.values:
        x_date.append(foreign_crued[0])
        y_foreign_crued.append(foreign_crued[1])
    for foreign_died in ws_foreign_died.values:
        y_foreign_died.append(foreign_died[1])

    fte_map = (
        Line(init_opts=opts.InitOpts(height='420px'))
            .add_xaxis(xaxis_data=x_date)
            .add_yaxis(
            series_name="境外累计确诊人数",
            y_axis=y_foreign_confirmed,
            label_opts=opts.LabelOpts(is_show=False),
            )
            .add_xaxis(xaxis_data=x_date)
            .add_yaxis(
            series_name="境外现有确诊人数",
            y_axis=y_foreign_surplus,
            label_opts=opts.LabelOpts(is_show=False),
            )
            .add_yaxis(
            series_name="境外累计治愈人数",
            y_axis=y_foreign_crued,
            label_opts=opts.LabelOpts(is_show=False),
            )
            .add_yaxis(
            series_name="境外累计死亡人数",
            y_axis=y_foreign_died,
            label_opts=opts.LabelOpts(is_show=False),
            )
            .set_global_opts(
            title_opts=opts.TitleOpts(title="境外COVID-19每日数据走势"),
            legend_opts=opts.LegendOpts(pos_bottom="bottom", orient='horizontal'),
            tooltip_opts=opts.TooltipOpts(trigger="axis"),
            yaxis_opts=opts.AxisOpts(
                type_="value",
                axistick_opts=opts.AxisTickOpts(is_show=True),
                splitline_opts=opts.SplitLineOpts(is_show=True),
            ),
            xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
            )
        .dump_options_with_quotes()
        )
    return fte_map

设置路由

在echarts目录下新建urls.py:

from django.urls import path
from . import views
app_name = 'echarts'
urlpatterns = [
    path('covid-19/ctmap/', views.CtView.as_view(),name = 'covid_19_ctmap'),
    path('covid-19/csmap/', views.CsView.as_view(),name = 'covid_19_csmap'),
    path('covid-19/fimap/', views.CdView.as_view(),name = 'covid_19_fimap'),
    path('covid-19/gtmap/', views.GtView.as_view(),name = 'covid_19_gtmap'),
    path('covid-19/gsmap/', views.GsView.as_view(),name = 'covid_19_gsmap'),
    path('covid-19/ftemap/', views.GdView.as_view(),name = 'covid_19_ftemap'),
    path('covid-19/',views.IndexView.as_view(),name = 'covid_19'),
    path('covid-19/update/',views.update,name = 'covid_19_update'),
]

引入静态文件

修改myproject/settings.py,在末尾添加如下内容:

STATICFILES_DIRS = (
    os.path.join(BASE_DIR,'static'),
)

在根目录新建static目录,static目录中新建echarts目录,将静态文件放进去。
本文所需的静态文件可在Github获取。

HTML模版

修改myproject/settings.py,更新TEMPLATES:
在DIRS中添加:os.path.join(BASE_DIR,'templates')

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

在根目录下新建templates文件夹,在templates中新建echarts文件夹,在其中新建covid_19.html:

<!DOCTYEP html>
{% load static %}
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>疫情大数据报告</title>
    <script src="{% static 'echarts/jquery/jquery.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'echarts/js/echarts.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'echarts/js/maps/china.js' %}"></script>
    <script type="text/javascript" src="{% static 'echarts/js/maps/world.js' %}"></script>
    <link rel="stylesheet" href="{% static 'echarts/bootstrap/css/bootstrap.min.css' %}">
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1-lts/dist/umd/popper.min.js"></script>
    <script src="{% static 'echarts/jquery/jquery-3.5.1.js' %}"></script>
    <!--script src="{% static 'echarts/layer/layer.js' %}"></script-->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
      <div class="container">
        <p class="navbar-brand">疫情大数据报告</p>
        <div>
          <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link" href="{% url 'echarts:covid_19_update' %}">更新</a>
            </li>

            <li class="nav-item">
              <a class="nav-link" href="{% url 'home' %}">返回</a>
            </li>
          </ul>
        </div>

      </div>
    </nav>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-2">
            <a class="nav-link" href="https://github.com/ljc545w/myproject">Github</a>
        </div>
        <div class="col-9">
            <div id="map1" style="height:550px;"></div>
            <div id="map2" style="height:550px;"></div>
            <div id="map3" style="height:370px;"></div>
            <div id="map4" style="height:550px;"></div>
            <div id="map5" style="height:550px;"></div>
            <div id="map6" style="height:370px;"></div>
        </div>
        <div class="col-1"></div>
    </div>
</div>
<script>
    var chart1 = echarts.init(document.getElementById('map1'),'write',{renderer:'canvas'});
    var chart2 = echarts.init(document.getElementById('map2'),'write',{renderer:'canvas'});
    var chart3 = echarts.init(document.getElementById('map3'),'write',{renderer:'canvas'});
    var chart4 = echarts.init(document.getElementById('map4'),'write',{renderer:'canvas'});
    var chart5 = echarts.init(document.getElementById('map5'),'write',{renderer:'canvas'});
    var chart6 = echarts.init(document.getElementById('map6'),'write',{renderer:'canvas'});

    $(
        function() {
            fetchData(chart1,"ctmap");
            fetchData(chart2,"csmap");
            fetchData(chart3,"fimap");
            fetchData(chart4,"gtmap");
            fetchData(chart5,"gsmap");
            fetchData(chart6,"ftemap");
        }
    );

    function fetchData(chart,string) {
        $.ajax({
            type:"GET",
            url:"/echarts/covid-19/" + string,
            dataType:'json',
            success:function (result) {
               chart.setOption(result.data);
           }
        });
    }
</script>
</body>
</html>

启动项目

#先需要运行一次data_get.py获取json文件
python /echarts/data_get.py
#启动
python manage.py runserver

在浏览器访问127.0.0.1:8000/echarts/covid-19/即可看到.

写在后面

虽然已经成功整合,在本地运行良好,但是服务器端存在缓存问题,点击更新后无法显示最新数据,需要多次刷新才行,这个问题留在以后解决。