全球疫情可视化展示
今天是一个缅怀在抗疫中牺牲烈士的日子。身为软工系的一名学生,应该做些东西!来纪念这次不一样的节日!
全球疫情数据可视化展示:
网页地址:http://3031k7q292.qicp.vip/yiqing/WorldData.jsp
(由于此web网站运用的是内网穿透访问,在服务器未开启的情况下无法访问网页)
一:任务目标
1、实现全球疫情数据的爬取
2、实现疫情数据的展示
3、实现疫情数据可视化展示
4、实现外网访问
二:效果图
三:源代码
JSP:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 4 <%@page import="com.bean.Data" %> 5 <!DOCTYPE html> 6 <html> 7 <head> 8 <meta charset="ISO-8859-1"> 9 <title>世界疫情</title> 10 </head> 11 12 <meta charset="UTF-8"> 13 <link type="text/css" rel="stylesheet" href="css/style.css"> 14 <script src="js/jquery-3.4.1.min.js"></script> 15 <script src="js/echarts.min.js"></script> 16 <script src="js/world.js"></script> 17 18 <style> 19 *{margin:0;padding:0} 20 html,body{ 21 width:100%; 22 height:100%; 23 } 24 #main{ 25 width:1500px; 26 height:1350px; 27 margin: 450px auto; 28 border:1px solid #ddd; 29 } 30 /*默认长宽比0.75*/ 31 </style> 32 </head> 33 34 <body onload="checkfind()"> 35 <br> 36 <h1>疫情统计表</h1> 37 <br> 38 <br> 39 <br> 40 <div id="h"> 41 <span> 42 <script>document.write(time);</script> 43 </span> 44 </div> 45 46 <div id="table"> 47 48 49 50 </div> 51 <div id="main"> 52 53 </div> 54 <script type="text/javascript"> 55 56 57 function randomValue() { 58 return Math.round(Math.random()*1000); 59 } 60 var dt; 61 var data = new Array(0); 62 myDate = new Date(); 63 var y = myDate.getFullYear(); 64 var m = myDate.getMonth()+1;//获取当前月份的日期 65 if(m<10){ 66 m='0'+m; 67 } 68 var d = myDate.getDate(); 69 if(d<10){ 70 d='0'+d; 71 } 72 var time = y+"-"+m+"-"+d; 73 $("#h").html(time); 74 function checkfind() { 75 76 77 if(time=="") 78 { 79 alert("请输入时间!"); 80 return ; 81 } 82 else{ 83 84 85 $.ajax({ 86 url : "WorldDataServlet", 87 async : true, 88 type : "POST", 89 data : { 90 "time" : time 91 }, 92 dataType : "json", 93 success : function(json) { 94 95 96 for (var i = 0; i < json.length; i++) { 97 var d = {}; 98 99 d["name"] = json[i].Province; 100 d["value"] = json[i].Confirmed_num; 101 d["yisi_num"] = json[i].Yisi_num; 102 d["cured_num"] = json[i].Cured_num; 103 d["dead_num"] = json[i].Dead_num; 104 105 106 data.push(d); 107 } 108 109 110 var myChart = echarts.init(document.getElementById('main')); 111 function randomValue() { 112 return Math.round(Math.random()*1000); 113 } 114 var optionMap = { 115 backgroundColor : '#FFFFFF', 116 title : { 117 text : '世界疫情', 118 subtext : '', 119 x : 'center' 120 }, 121 tooltip : { 122 formatter : function(params) { 123 return params.name + '<br/>' + '确诊人数 : ' 124 + params.value + '<br/>' + '死亡人数 : ' 125 + params['data'].dead_num + '<br/>' + '治愈人数 : ' 126 + params['data'].cured_num + '<br/>'+ '疑似患者人数 : ' 127 + params['data'].yisi_num; 128 } 129 }, 130 131 //左侧小导航图标 132 visualMap: { 133 min: 0, 134 max: 350000, 135 left: 'left', 136 top: 'bottom', 137 text: ['高','低'],//取值范围的文字 138 inRange: { 139 color: ['yellow','orangered','red']//取值范围的颜色 140 }, 141 show:true//图注 142 }, 143 144 145 //配置属性 146 series : [ { 147 type : 'map', 148 mapType : 'world', 149 label : { 150 show : true 151 }, 152 153 zoom: 1, //当前视角的缩放比例 154 155 roam: true, //是否开启平游或缩放 156 157 scaleLimit: { //滚轮缩放的极限控制 158 159 min: 1, 160 161 max: 2 162 163 }, 164 data : data, 165 nameMap : { 166 167 'Singapore Rep.':'新加坡', 168 'Dominican Rep.':'多米尼加', 169 'Palestine':'巴勒斯坦', 170 'Bahamas':'巴哈马', 171 'Timor-Leste':'东帝汶', 172 'Afghanistan':'阿富汗', 173 'Guinea-Bissau':'几内亚比绍', 174 "Côte d'Ivoire":'科特迪瓦', 175 'Siachen Glacier':'锡亚琴冰川', 176 "Br. Indian Ocean Ter.":'英属印度洋领土', 177 'Angola':'安哥拉', 178 'Albania':'阿尔巴尼亚', 179 'United Arab Emirates':'阿联酋', 180 'Argentina':'阿根廷', 181 'Armenia':'亚美尼亚', 182 'French Southern and Antarctic Lands':'法属南半球和南极领地', 183 'Australia':'澳大利亚', 184 'Austria':'奥地利', 185 'Azerbaijan':'阿塞拜疆', 186 'Burundi':'布隆迪', 187 'Belgium':'比利时', 188 'Benin':'贝宁', 189 'Burkina Faso':'布基纳法索', 190 'Bangladesh':'孟加拉国', 191 'Bulgaria':'保加利亚', 192 'The Bahamas':'巴哈马', 193 'Bosnia and Herz.':'波斯尼亚和黑塞哥维那', 194 'Belarus':'白俄罗斯', 195 'Belize':'伯利兹', 196 'Bermuda':'百慕大', 197 'Bolivia':'玻利维亚', 198 'Brazil':'巴西', 199 'Brunei':'文莱', 200 'Bhutan':'不丹', 201 'Botswana':'博茨瓦纳', 202 'Central African Rep.':'中非', 203 'Canada':'加拿大', 204 'Switzerland':'瑞士', 205 'Chile':'智利', 206 'China':'中国', 207 'Ivory Coast':'象牙海岸', 208 'Cameroon':'喀麦隆', 209 'Dem. Rep. Congo':'刚果民主共和国', 210 'Congo':'刚果', 211 'Colombia':'哥伦比亚', 212 'Costa Rica':'哥斯达黎加', 213 'Cuba':'古巴', 214 'N. Cyprus':'北塞浦路斯', 215 'Cyprus':'塞浦路斯', 216 'Czech Rep.':'捷克', 217 'Germany':'德国', 218 'Djibouti':'吉布提', 219 'Denmark':'丹麦', 220 'Algeria':'阿尔及利亚', 221 'Ecuador':'厄瓜多尔', 222 'Egypt':'埃及', 223 'Eritrea':'厄立特里亚', 224 'Spain':'西班牙', 225 'Estonia':'爱沙尼亚', 226 'Ethiopia':'埃塞俄比亚', 227 'Finland':'芬兰', 228 'Fiji':'斐', 229 'Falkland Islands':'福克兰群岛', 230 'France':'法国', 231 'Gabon':'加蓬', 232 'United Kingdom':'英国', 233 'Georgia':'格鲁吉亚', 234 'Ghana':'加纳', 235 'Guinea':'几内亚', 236 'Gambia':'冈比亚', 237 'Guinea Bissau':'几内亚比绍', 238 'Eq. Guinea':'赤道几内亚', 239 'Greece':'希腊', 240 'Greenland':'格陵兰', 241 'Guatemala':'危地马拉', 242 'French Guiana':'法属圭亚那', 243 'Guyana':'圭亚那', 244 'Honduras':'洪都拉斯', 245 'Croatia':'克罗地亚', 246 'Haiti':'海地', 247 'Hungary':'匈牙利', 248 'Indonesia':'印度尼西亚', 249 'India':'印度', 250 'Ireland':'爱尔兰', 251 'Iran':'伊朗', 252 'Iraq':'伊拉克', 253 'Iceland':'冰岛', 254 'Israel':'以色列', 255 'Italy':'意大利', 256 'Jamaica':'牙买加', 257 'Jordan':'约旦', 258 'Japan':'日本', 259 'Kazakhstan':'哈萨克斯坦', 260 'Kenya':'肯尼亚', 261 'Kyrgyzstan':'吉尔吉斯斯坦', 262 'Cambodia':'柬埔寨', 263 'Korea':'韩国', 264 'Kosovo':'科索沃', 265 'Kuwait':'科威特', 266 'Lao PDR':'老挝', 267 'Lebanon':'黎巴嫩', 268 'Liberia':'利比里亚', 269 'Libya':'利比亚', 270 'Sri Lanka':'斯里兰卡', 271 'Lesotho':'莱索托', 272 'Lithuania':'立陶宛', 273 'Luxembourg':'卢森堡', 274 'Latvia':'拉脱维亚', 275 'Morocco':'摩洛哥', 276 'Moldova':'摩尔多瓦', 277 'Madagascar':'马达加斯加', 278 'Mexico':'墨西哥', 279 'Macedonia':'马其顿', 280 'Mali':'马里', 281 'Myanmar':'缅甸', 282 'Montenegro':'黑山', 283 'Mongolia':'蒙古', 284 'Mozambique':'莫桑比克', 285 'Mauritania':'毛里塔尼亚', 286 'Malawi':'马拉维', 287 'Malaysia':'马来西亚', 288 'Namibia':'纳米比亚', 289 'New Caledonia':'新喀里多尼亚', 290 'Niger':'尼日尔', 291 'Nigeria':'尼日利亚', 292 'Nicaragua':'尼加拉瓜', 293 'Netherlands':'荷兰', 294 'Norway':'挪威', 295 'Nepal':'尼泊尔', 296 'New Zealand':'新西兰', 297 'Oman':'阿曼', 298 'Pakistan':'巴基斯坦', 299 'Panama':'巴拿马', 300 'Peru':'秘鲁', 301 'Philippines':'菲律宾', 302 'Papua New Guinea':'巴布亚新几内亚', 303 'Poland':'波兰', 304 'Puerto Rico':'波多黎各', 305 'Dem. Rep. Korea':'朝鲜', 306 'Portugal':'葡萄牙', 307 'Paraguay':'巴拉圭', 308 'Qatar':'卡塔尔', 309 'Romania':'罗马尼亚', 310 'Russia':'俄罗斯', 311 'Rwanda':'卢旺达', 312 'W. Sahara':'西撒哈拉', 313 'Saudi Arabia':'沙特阿拉伯', 314 'Sudan':'苏丹', 315 'S. Sudan':'南苏丹', 316 'Senegal':'塞内加尔', 317 'Solomon Is.':'所罗门群岛', 318 'Sierra Leone':'塞拉利昂', 319 'El Salvador':'萨尔瓦多', 320 'Somaliland':'索马里兰', 321 'Somalia':'索马里', 322 'Serbia':'塞尔维亚', 323 'Suriname':'苏里南', 324 'Slovakia':'斯洛伐克', 325 'Slovenia':'斯洛文尼亚', 326 'Sweden':'瑞典', 327 'Swaziland':'斯威士兰', 328 'Syria':'叙利亚', 329 'Chad':'乍得', 330 'Togo':'多哥', 331 'Thailand':'泰国', 332 'Tajikistan':'塔吉克斯坦', 333 'Turkmenistan':'土库曼斯坦', 334 'East Timor':'东帝汶', 335 'Trinidad and Tobago':'特里尼达和多巴哥', 336 'Tunisia':'突尼斯', 337 'Turkey':'土耳其', 338 'Tanzania':'坦桑尼亚', 339 'Uganda':'乌干达', 340 'Ukraine':'乌克兰', 341 'Uruguay':'乌拉圭', 342 'United States':'美国', 343 'Uzbekistan':'乌兹别克斯坦', 344 'Venezuela':'委内瑞拉', 345 'Vietnam':'越南', 346 'Vanuatu':'瓦努阿图', 347 'West Bank':'西岸', 348 'Yemen':'也门', 349 'South Africa':'南非', 350 'Zambia':'赞比亚', 351 'Zimbabwe':'津巴布韦' 352 } 353 354 } ] 355 }; 356 myChart.setOption(optionMap); 357 myChart.on('click', function (params) { 358 alert(params.name); 359 }); 360 361 362 alert("成功!"); 363 364 365 //使用制定的配置项和数据显示图表 366 createShowingTable(json); 367 368 }, 369 error : function() { 370 alert("请求失败"); 371 }, 372 }); 373 } 374 375 } 376 /* setTimeout(function () { 377 myChart.setOption({ 378 series : [ 379 { 380 name: '信息量', 381 type: 'map', 382 geoIndex: 0, 383 data:dataList 384 } 385 ] 386 }); 387 },1000)*/ 388 function createShowingTable(json) { 389 var tableStr = "<table id='gradient-style' style='background-color:blue; width:100%; height:100%'>"; 390 tableStr = tableStr 391 + "<tr>" 392 393 +"<td >国家</td>" 394 +"<td >确诊人数</td>" 395 +"<td >疑似人数</td>" 396 +"<td >治愈人数</td>" 397 +"<td >死亡人数</td>" 398 +"</tr>"; 399 var len = data.length; 400 for ( var i = 0; i < len; i++) { 401 tableStr = tableStr + "<tr>" 402 403 +"<td>"+ json[i].Province +"</td>" 404 +"<td>"+ json[i].Confirmed_num + "</td>" 405 + "<td>"+ json[i].Yisi_num + "</td>" 406 + "<td>"+ json[i].Cured_num + "</td>" 407 +"<td>"+json[i].Dead_num+"</td>" 408 +"</tr>"; 409 } 410 411 tableStr = tableStr + "</table>"; 412 //添加到div中 413 $("#table").html(tableStr);1 414 } 415 416 </script> 417 418 419 </body> 420 </html>
Python爬虫源码:
import json import numpy as np import pymysql import requests from bs4 import BeautifulSoup import datetime url = 'https://ncov.dxy.cn/ncovh5/view/pneumonia?from=timeline&isappinstalled=0' #请求地址 headers = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'}#创建头部信息 response = requests.get(url,headers = headers) #发送网络请求 #print(response.content.decode('utf-8'))#以字节流形式打印网页源码 content = response.content.decode('utf-8') #print(content) soup = BeautifulSoup(content, 'html.parser') listA = soup.find_all(name='script',attrs={"id":"getAreaStat"}) #世界确诊getAreaStat listB = soup.find_all(name='script',attrs={"id":"getListByCountryTypeService2true"}) #listA = soup.find_all(name='div',attrs={"class":"c-touchable-feedback c-touchable-feedback-no-default"}) account = str(listA) world_messages = str(listB)[95:-21] messages = account[52:-21] messages_json = json.loads(messages) print(world_messages) world_messages_json = json.loads(world_messages) valuesList = [] cityList = [] worldList = [] now_time = datetime.datetime.now().strftime('%Y-%m-%d') for k in range(len(world_messages_json)): worldvalue = (now_time, world_messages_json[k].get('countryType'),world_messages_json[k].get('continents'),world_messages_json[k].get('provinceId'),world_messages_json[k].get('provinceName'), world_messages_json[k].get('provinceShortName'),world_messages_json[k].get('cityName'),world_messages_json[k].get('currentConfirmedCount'),world_messages_json[k].get('confirmedCount'), world_messages_json[k].get('suspectedCount'),world_messages_json[k].get('curedCount'),world_messages_json[k].get('deadCount'),world_messages_json[k].get('locationId'), world_messages_json[k].get('countryShortCode'),) worldList.append(worldvalue) for i in range(len(messages_json)): #value = messages_json[i] value = (now_time,messages_json[i].get('provinceName'),messages_json[i].get('provinceShortName'),messages_json[i].get('currentConfirmedCount'),messages_json[i].get('confirmedCount'),messages_json[i].get('suspectedCount'),messages_json[i].get('curedCount'),messages_json[i].get('deadCount'),messages_json[i].get('comment'),messages_json[i].get('locationId'),messages_json[i].get('statisticsData')) valuesList.append(value) cityValue = messages_json[i].get('cities') #print(cityValue) for j in range(len(cityValue)): cityValueList = (cityValue[j].get('cityName'),cityValue[j].get('currentConfirmedCount'),cityValue[j].get('confirmedCount'),cityValue[j].get('suspectedCount'),cityValue[j].get('curedCount'),cityValue[j].get('deadCount'),cityValue[j].get('locationId'),messages_json[i].get('provinceShortName')) #print(cityValueList) cityList.append(cityValueList) #cityList.append(cityValue) db = pymysql.connect("localhost", "root", "123456", "mytest", charset='utf8') cursor = db.cursor() array = np.asarray(valuesList[0]) #sql_clean_world = "TRUNCATE TABLE world_map" sql_clean_city = "TRUNCATE TABLE city_map" sql_clean_json = "TRUNCATE TABLE province_data_from_json" sql_clean_province = "TRUNCATE TABLE province_map" sql_clean_world = "TRUNCATE TABLE world_map" sql1 = "INSERT INTO city_map values (%s,%s,%s,%s,%s,%s,%s,%s)" sql_world = "INSERT INTO world_map values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)" #sql = "INSERT INTO province_map values (0,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s') " sql = "INSERT INTO province_map values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) " #sql = "INSERT INTO province_map (provinceName,provinceShortName,correntConfirmedCount,confirmedCount,suspectedCount,curedCount,deadCount,comment,locationId,statisticsData) values (0,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s') " #sql = """INSERT INTO province_map (provinceName,provinceShortName,correntConfirmedCount,confirmedCount,suspectedCount,curedCount,deadCount,comment,locationId,statisticsData) values ('湖北省', '湖北', 43334, 64786, 0, 18889, 2563, '', 420000, 'https://file1.dxycdn.com/2020/0223/618/3398299751673487511-135.json')""" value_tuple = tuple(valuesList) cityTuple = tuple(cityList) worldTuple = tuple(worldList) print(worldTuple) print(tuple(value_tuple)) try: #cursor.execute(sql_clean_city) #cursor.execute(sql_clean_province) #cursor.executemany(sql, value_tuple) #cursor.executemany(sql1,cityTuple) db.commit() except: print('执行失败,进入回调1') db.rollback() try: #cursor.execute(sql_clean_city) #cursor.execute(sql_clean_province) #cursor.execute(sql_clean_world) #cursor.executemany(sql, value_tuple) #cursor.executemany(sql1,cityTuple) cursor.executemany(sql_world, worldTuple) db.commit() except: print('执行失败,进入回调2') db.rollback() try: #cursor.execute(sql_clean_city) #cursor.execute(sql_clean_province) cursor.executemany(sql, value_tuple) #cursor.executemany(sql1,cityTuple) db.commit() except: print('执行失败,进入回调3') db.rollback() try: #cursor.execute(sql_clean_city) #cursor.execute(sql_clean_province) #cursor.executemany(sql, value_tuple) #cursor.executemany(sql1,cityTuple) db.commit() except: print('执行失败,进入回调4') db.rollback() #print(messages_json) #print(account[52:-21]) # soupDiv = BeautifulSoup(listA,'html.parser') # listB = soupDiv.find_all(name='div',attrs={"class":"c-gap-bottom-zero c-line-clamp2"}) #for i in listA: #print(i) #listA[12] #print(listA) db.close()
四:遇到的问题
首先是world.js资源难找,附上我的资源链接:
https://pan.baidu.com/s/1tZkCm06wZpfH4MBvf9gDnw 提取码: bvy3
然后就是CSS部分学的不太好,导致未能达到预期效果
五:时间表
学习时长 | 6h |
预计时常 | 8h |
实际时常 | 10h |
知识点 | 内网穿透 |