├── .gitattributes
├── README.md
├── flask
├── templates
│ ├── jupyter_lab.html
│ ├── nteract.html
│ ├── simple_chart.html
│ ├── image.html
│ ├── simple_page.html
│ ├── jupyter_notebook.html
│ ├── macro
│ ├── table.html
│ └── index.html
└── app.py
└── spider
└── dxy.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PythonSARI
2 | Python疫情监控系统源码
3 |
4 | 配套视频课程地址:https://edu.csdn.net/course/play/23753
5 |
6 |
--------------------------------------------------------------------------------
/flask/templates/jupyter_lab.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% for c in charts %}
9 | {{ macro.render_chart_content(c) }}
10 | {% endfor %}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/flask/templates/nteract.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ macro.render_chart_dependencies(chart) }}
7 |
8 |
9 | {% for c in chart %}
10 | {{ macro.render_chart_content(c) }}
11 | {% endfor %}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/flask/templates/simple_chart.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 | {{ macro.render_chart_dependencies(chart) }}
8 |
9 |
10 | {{ macro.render_chart_content(chart) }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/flask/templates/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ chart.page_title }}
6 |
7 |
8 |
9 | {{ chart.title_opts.title }}
10 | {{ chart.title_opts.subtitle }}
11 | {% for c in chart %}
12 |
13 | {% endfor %}
14 |
15 |
16 |
--------------------------------------------------------------------------------
/flask/templates/simple_page.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 | {{ macro.render_chart_dependencies(chart) }}
8 |
9 |
10 |
11 |
12 | {% for c in chart %}
13 | {{ macro.render_chart_content(c) }}
14 | {% for _ in range(chart.page_interval) %}
{% endfor %}
15 | {% endfor %}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/flask/templates/jupyter_notebook.html:
--------------------------------------------------------------------------------
1 |
8 |
9 | {% for chart in charts %}
10 |
11 | {% endfor %}
12 |
13 |
14 |
31 |
--------------------------------------------------------------------------------
/flask/templates/macro:
--------------------------------------------------------------------------------
1 | {%- macro render_chart_content(c) -%}
2 |
3 |
20 | {%- endmacro %}
21 |
22 | {%- macro render_chart_dependencies(c) -%}
23 | {% for dep in c.dependencies %}
24 |
25 | {% endfor %}
26 | {%- endmacro %}
27 |
--------------------------------------------------------------------------------
/flask/templates/table.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ chart.page_title }}
6 |
7 |
8 |
60 |
61 | {{ chart.title_opts.title }}
62 | {{ chart.title_opts.subtitle }}
63 | {% for c in chart %}
64 | {{ c }}
65 | {% endfor %}
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/flask/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 全国疫情监控完整项目实战
6 |
7 |
8 |
9 |
10 |
11 |
12 |
27 |
28 |
29 |
44 |
45 |
46 |
61 |
62 |
77 |
78 |
131 |
132 |
全国疫情数据分布
133 |
134 |
135 | | 地区 |
136 | 确诊 |
137 | 死亡 |
138 | 治愈 |
139 |
140 | {% for i in content %}
141 |
142 | {% for j in i %}
143 | | {{ j }} |
144 | {% endfor %}
145 |
146 | {% endfor %}
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/spider/dxy.py:
--------------------------------------------------------------------------------
1 | from requests_html import HTMLSession
2 | import random
3 | import csv
4 | import json
5 | import datetime
6 |
7 | session = HTMLSession()
8 | USER_AGENTS = [
9 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
10 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
11 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
12 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
13 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
14 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
15 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
16 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
17 | "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
18 | "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
19 | "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
20 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
21 | "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
22 | ]
23 |
24 |
25 | def getTime():
26 | curtime = datetime.datetime.now()
27 | strtime = curtime.strftime('_%Y_%m_%d_%H_%M_%S')
28 | return strtime
29 |
30 |
31 | class DxySARI(object):
32 | def __init__(self):
33 | self.start = 0
34 | # self.headers = {'User-Agent': 'Mozilla/5.0(Windows NT 6.1; WOW64)'}
35 | self.headers = {"User-Agent": random.choice(USER_AGENTS)}
36 | self.dxyurl = 'https://3g.dxy.cn/newh5/view/pneumonia'
37 | self.items = []
38 | self.province_list = []
39 | self.city_list = []
40 | self.city_csv = '../data/city.csv'
41 | self.province_csv = '../data/province'
42 |
43 | # 页面数据 + 目标字符串
44 | def get_html_page(self):
45 | response = session.get(self.dxyurl)
46 | page = response.html.html
47 | # print(page)
48 | start = page.find("window.getAreaStat = [")
49 | # print(start)
50 | temp = page[start+22:]
51 | end = temp.find("]}catch(e){}")
52 | temp = temp[0:end]
53 | items = temp.split("]},")
54 | # 最后一个元素,特殊处理(尾部没有“,”号)
55 | last = items[-1]
56 | items.pop()
57 | newitems = []
58 | for item in items:
59 | item = item + "]}"
60 | newitems.append(item)
61 | # print(item)
62 | newitems.append(last)
63 |
64 | self.items = newitems
65 | return newitems
66 |
67 | # 数据结构化
68 | def get_detail_info(self,items):
69 | # 格式转换:字符串->JSON
70 | for item in items:
71 | js = json.loads(item)
72 | # print(js)
73 | # 格式转换:JSON->字典
74 | dc = {}
75 | dc = dict(js)
76 | # 省份数据(含城市)
77 | self.province_list.append(dc)
78 | # 城市数据(前缀增加省份)
79 | # print(self.province_list)
80 | cities = dc["cities"]
81 | # 需要增加判断,处理列表为空的情况(直辖市和特区的问题)
82 | if cities:
83 | for city in cities:
84 | city["provinceName"] = dc["provinceName"]
85 | self.city_list.append(city)
86 | # print(city.keys())
87 | # 保存至文件
88 | # print(self.province_list)
89 | # print(self.city_list)
90 |
91 | # 省份数据写入CSV:城市数据作为整体存储,不使用
92 | def write_province_csv(self):
93 | filename = self.province_csv + getTime() + '.csv'
94 | # print(filename)
95 | header = ['provinceName', 'provinceShortName', 'confirmedCount', 'suspectedCount', 'curedCount', 'deadCount', 'comment','cities']
96 | with open(filename, 'w', newline='',encoding='utf-8-sig') as csvfile:
97 | file_pro = csv.writer(csvfile)
98 | file_pro.writerow(header)
99 | try:
100 | for item in self.province_list:
101 | print(item.values())
102 | file_pro.writerow(item.values())
103 | except Exception as e:
104 | print('Province数据写入异常' + e + '异常')
105 |
106 | # 城市数据写入CSV
107 | def write_city_csv(self):
108 | filename = self.city_csv + getTime() + '.csv'
109 | print(filename)
110 | header = [['cityName', 'confirmedCount', 'suspectedCount', 'curedCount', 'deadCount', 'provinceName']]
111 | with open(filename, 'w', newline='',encoding='utf-8-sig') as csvfile:
112 | file_city = csv.writer(csvfile)
113 | file_city.writerow(header)
114 | try:
115 | for item in self.city_list:
116 | print(item.values())
117 | file_city.writerow(item.values())
118 | except Exception as e:
119 | print('City数据写入异常' + e + '异常')
120 |
121 |
122 | # 主函数
123 | if __name__ == '__main__':
124 | dxy = DxySARI()
125 | page = dxy.get_html_page()
126 | dxy.get_detail_info(page)
127 | #dxy.write_province_csv()
128 | #dxy.write_city_csv()
129 |
130 |
131 |
--------------------------------------------------------------------------------
/flask/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template,jsonify
2 | from pyecharts import options as opts
3 | from pyecharts.charts import Map
4 | from pyecharts.charts import Line
5 | from pyecharts.globals import ChartType, SymbolType
6 | from pyecharts.globals import ThemeType
7 |
8 | from pyecharts.components import Table
9 | from pyecharts.options import ComponentTitleOpts
10 | import json
11 |
12 | app = Flask(__name__, static_folder="templates",)
13 |
14 |
15 | # 第一部分:图表创建
16 | # 01.实时疫情地图
17 | def map_base()->Map:
18 | # 数据暂时写死:后续可以结合实时数据,进行更新
19 |
20 | # 省和直辖市
21 | province_distribution = {'河南': 566, '北京': 212, '河北': 21, '辽宁': 12, '江西': 391, '上海': 203, '安徽': 408, '江苏': 271,
22 | '湖南': 521, '浙江': 724, '海南': 2, '广东': 725, '湖北': 11177, '黑龙江': 121, '澳门': 1, '陕西': 128, '四川': 254,
23 | '内蒙古': 3, '重庆': 312, '云南': 6, '贵州': 2, '吉林': 3, '山西': 12, '山东': 259, '福建': 179, '青海': 1,
24 | '天津': 1, '其他': 1}
25 | provice = list(province_distribution.keys())
26 | values = list(province_distribution.values())
27 |
28 | c = (
29 | Map()
30 | .add("确诊人数", [list(z) for z in zip(provice,values)], "china")
31 | .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
32 | .set_global_opts(
33 | title_opts=opts.TitleOpts(title="全国实时疫情分布地图"),
34 | visualmap_opts=opts.VisualMapOpts(max_= 1000),)
35 | )
36 | return c
37 |
38 |
39 | # 02. 疫情新增趋势图
40 | def conf_new_base() -> Line:
41 | # 静态数据
42 | dataY1 = [59, 77, 149, 131, 256, 444, 688, 769, 1771, 1459, 1737, 1982, 2102, 2590, 2829, 3235, 3887]
43 | dataY2 = [59, 27, 149, 131, 680,1118,1309, 3806, 2077, 3248, 4148, 4812, 5019, 4562, 5173, 5072, 3971]
44 | dataX = ['2020.01.18', '2020.01.19', '2020.01.20', '2020.01.21', '2020.01.22','2020.01.23','2020.01.24','2020.01.25',
45 | '2020.01.26', '2020.01.27', '2020.01.28', '2020.01.29', '2020.01.30','2020.01.31','2020.02.01','2020.02.02',
46 | '2020.02.03', '2020.02.04']
47 | c = (
48 | Line(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
49 | .add_xaxis(dataX)
50 | .add_yaxis("新增确诊", dataY1, is_smooth=True)
51 | .add_yaxis("新增疑似", dataY2, is_smooth=True)
52 | .set_global_opts(
53 | title_opts=opts.TitleOpts(title="全国疫情新增确诊/疑似趋势图"),
54 | yaxis_opts=opts.AxisOpts(
55 | type_="value",
56 | axistick_opts=opts.AxisTickOpts(is_show=True),
57 | splitline_opts=opts.SplitLineOpts(is_show=True),
58 | ),
59 | xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
60 | )
61 | )
62 | return c
63 |
64 |
65 | # 03. 全国累计确诊/疑似趋势图
66 | def conf_total_base() -> Line:
67 | # 静态数据
68 | dataY1 = [291, 440, 571, 830, 1287, 1975, 2744, 4515, 5974, 7711, 9692, 11791, 14380, 17205, 20438, 24324]
69 | dataY2 = [54, 37, 393, 1072, 1965, 2684, 5794, 6973, 9239, 12167, 15238, 17988, 19544, 21558,23214, 23260]
70 |
71 | dataX = ['2020.01.20', '2020.01.21', '2020.01.22', '2020.01.23', '2020.01.24', '2020.01.25', '2020.01.26',
72 | '2020.01.27', '2020.01.28', '2020.01.29', '2020.01.30', '2020.01.31', '2020.02.01', '2020.02.02',
73 | '2020.02.03', '2020.02.04']
74 | c = (
75 | Line(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
76 | .add_xaxis(dataX)
77 | .add_yaxis("累计确诊", dataY1, is_smooth=True)
78 | .add_yaxis("累计疑似", dataY2, is_smooth=True)
79 | .set_global_opts(
80 | title_opts=opts.TitleOpts(title="全国疫情累计确诊/疑似趋势图"),
81 | yaxis_opts=opts.AxisOpts(
82 | type_="value",
83 | axistick_opts=opts.AxisTickOpts(is_show=True),
84 | splitline_opts=opts.SplitLineOpts(is_show=True),
85 | ),
86 | xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
87 | )
88 | )
89 | return c
90 |
91 |
92 | # 04. 全国累计死亡/治愈趋势图
93 | def dead_total_base() -> Line:
94 | # 静态数据
95 | dataY1 = [0, 9, 17, 25, 41, 56, 80, 106, 132, 170, 213, 259, 304, 361, 425, 490]
96 | dataY2 = [0, 0, 0, 34, 38, 49, 51, 60, 103, 124, 171, 243, 328, 475, 632, 892]
97 |
98 | dataX = ['2020.01.20', '2020.01.21', '2020.01.22', '2020.01.23', '2020.01.24', '2020.01.25', '2020.01.26',
99 | '2020.01.27', '2020.01.28', '2020.01.29', '2020.01.30', '2020.01.31', '2020.02.01', '2020.02.02',
100 | '2020.02.03','2020.02.04']
101 | c = (
102 | Line(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
103 | .add_xaxis(dataX)
104 | .add_yaxis("累计死亡", dataY1, is_smooth=True)
105 | .add_yaxis("累计治愈", dataY2, is_smooth=True)
106 | .set_global_opts(
107 | title_opts=opts.TitleOpts(title="全国疫情累计死亡/治愈趋势图"),
108 | yaxis_opts=opts.AxisOpts(
109 | type_="value",
110 | axistick_opts=opts.AxisTickOpts(is_show=True),
111 | splitline_opts=opts.SplitLineOpts(is_show=True),
112 | ),
113 | xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
114 | )
115 | )
116 | return c
117 |
118 |
119 | # 05. 全国各省市数据明细
120 | def table_base():
121 | table = Table()
122 |
123 | headers = ["地区", "确诊", "死亡", "治愈"]
124 | rows = [
125 | ["湖北", 13522, 414, 397],
126 | ["浙江", 829, 0, 60],
127 | ["广东", 813, 0, 24],
128 | ["河南", 675, 2, 20],
129 | ["湖南", 593, 0, 29],
130 | ["安徽", 480, 0, 20],
131 | ["江西", 476, 2, 19],
132 | ["重庆", 344, 2, 9]
133 | ]
134 | table.add(headers, rows).set_global_opts(
135 | title_opts=ComponentTitleOpts(title="Table", subtitle="副标题")
136 | )
137 | # table.render("table_base.html")
138 | return rows
139 |
140 |
141 | # 第二部分:路由配置
142 | @app.route("/")
143 | def index():
144 | content = table_base()
145 | return render_template("index.html", content=content)
146 |
147 |
148 | @app.route("/mapChart")
149 | def get_map_chart():
150 | c = map_base()
151 | return c.dump_options_with_quotes()
152 |
153 |
154 | @app.route("/confChart")
155 | def get_conf_chart():
156 | c = conf_new_base()
157 | return c.dump_options_with_quotes()
158 |
159 |
160 | @app.route("/confTotalChart")
161 | def get_conf_total_chart():
162 | c = conf_total_base()
163 | return c.dump_options_with_quotes()
164 |
165 |
166 | @app.route("/deadTotalChart")
167 | def get_dead_total_chart():
168 | c = dead_total_base()
169 | return c.dump_options_with_quotes()
170 |
171 |
172 | # 主函数
173 | if __name__ == "__main__":
174 | app.run()
175 |
--------------------------------------------------------------------------------