新型冠狀病毒來襲,在此全國人民齊心抗疫之際,身為軟件工程專業的一員,也要充分發揮專業能力,為疫情做點什么。
到目前為止,很多網站或者APP都新增了疫情數據分析這樣一個欄目,這樣一個專欄幫助用戶充分了解全國各地乃至全世界的疫情情況,今天就特地做了這樣的一個實戰項目,來實現疫情數據的實時可視化。
首先,設計思路如下:
(1)使用爬蟲爬取網站中的數據並存入數據庫
(2)使用java做后端將數據庫的內容傳送到前端
(3)前端使用echarts框架對數據進行可視化
在爬取數據的時候,還是經歷了一些小插曲,我爬取的網站如下:
https://news.qq.com/zt2020/page/feiyan.htm#/
首先就是按照爬蟲套路查看網頁源代碼,可以看到效果如下:

很容易發現,class為areaBox的 tr 標簽中保存的是各省的數據,其中的area為省份名稱,其余不包含class的 td保存的是疫情的各項人數,而class為 city 的tr中保存的是各個城市的數據,其中,area為城市名稱,其余td保存的疫情各項的人數。
在這樣的簡單分析之后,頓時感覺這實在是太小兒科了,兩個嵌套for循環就可以搞定,於是開始編寫爬蟲,初始測試爬蟲代碼如下:
import requests from bs4 import BeautifulSoup url = "https://news.qq.com/zt2020/page/feiyan.htm#/" r = requests.get(url) html = r.text soup = BeautifulSoup(html,"lxml") print(soup.prettify())
運行之后,打印數據是這樣的

只有一坨 js 代碼還有頁面頭部的幾個div,並沒有找到我們想要的標簽。遲疑了一瞬間,恍然醒悟數據應該是通過js發送請求之后從后台傳回來的。直接看這一段HTML代碼,果然什么都看不出來。沒辦法,F12去瀏覽器控制台看看到底發生了什么數據交換的操作吧。(微軟的瀏覽器用的有點雞肋,這里切一下Chrome)可以看到服務器返回了這些數據:

可以看到除了無關緊要的png圖片等資源,還返回了這樣一堆js文件,一看名字,這些get***Info可能就是我們需要的,檢查一下它的地址,然后復制打開


瞬間激動了有木有!!這不就是現成的json數據么,還分析個球的HTML標簽,直接爬起來!!!
(激動過頭了)先分析一下json的格式和我們需要的“樹”,首先可以看到開頭有個name 中國的字樣,由於該網站支持其他國家的數據,如下:
所以我們解析json的時候就要通過它來篩選本國的數據,那么,在中國下面,又有這樣一些數據:

很顯然,在name的子數據中,key為children的數據就是對應的各省份的數據(其他省份格式相同,不再給出截圖),與此同時,在省份下面的各個城市,同樣是以children標簽保存數據的

![]()
以武漢為例,today中是今日新增的確診,total中是截止到目前的總人數,也是我們需要的數據,簡單翻譯可知,confirm為確診人數,dead為死亡人數,heal為治愈人數,那么數據的更新時間在哪兒呢?json開頭我們可以看到這樣的字樣:

在該文件的其余地方均沒有類似時間字樣的key,不難推斷出,它就是最新的數據更新時間。
OK,到此為止,分析完成,代碼搞起來!
import requests import json from pymysql import * #連接數據庫的方法 def connectDB(): try: db=connect(host='localhost',port=3306,user='root',password='010218',db='payiqing_db') print("數據庫連接成功") return db except Exception as e: print(e) return NULL db=connectDB() #向數據庫中插入數據的方法 def insertInformation(db,table,Date,Province,City,Confirmed_num,Yisi_num,Cured_num,Dead_num,Code): cursor=db.cursor() try: cursor.execute("insert into %s(Date,Province,City,Confirmed_num,Yisi_num,Cured_num,Dead_num,Code) values('%s','%s','%s','%s','%s','%s','%s','%s')" % (table,Date,Province,City,Confirmed_num,Yisi_num,Cured_num,Dead_num,Code)) print("插入成功") db.commit() cursor.close() return True except Exception as e: print(e) db.rollback() return False def queryInformation(city): cursor = db.cursor() sql = "SELECT * FROM info WHERE City = '%s'" % (city) try: # print(sql) # 執行SQL語句 cursor.execute(sql) # 獲取所有記錄列表 results = cursor.fetchone() return results[-1] except Exception as e: print(e) db.rollback() def clearTable(date): cursor = db.cursor() sql="delete from info2 where Date = '%s'"% (date) try: print(sql) # 執行SQL語句 cursor.execute(sql) except Exception as e: print(e) db.rollback() def get_data(): url = 'https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5' json_text = requests.get(url).json() data = json.loads(json_text['data']) update_time = data['lastUpdateTime'] all_counties = data['areaTree'] all_list = [] for country_data in all_counties: if country_data['name'] == '中國': all_provinces = country_data['children'] for province_data in all_provinces: province_name = province_data['name'] all_cities = province_data['children'] for city_data in all_cities: city_name = city_data['name'] city_total = city_data['total'] province_result = {'province': province_name, 'city': city_name,'update_time': update_time} province_result.update(city_total) all_list.append(province_result) clearTable(all_list[0].get("update_time")) for info in all_list: print(info) insertInformation(db,"info2",info.get("update_time"),info.get("province"),info.get("city"),info.get("confirm"),info.get("suspect"),info.get("heal"),info.get("dead"),queryInformation(info.get("city"))) if __name__ == '__main__': get_data()
以上代碼包含數據庫的更新,讀者可根據自己數據庫的配置自行修改。
然后就是數據可視化了,這里只給出截圖吧,完整代碼后續會上傳到我的GitHub中。

最后,附上實現本次項目的PSP表格


