目前全球疫情仍然比較嚴重,爲了能清晰地看到疫情爆發以來至如今全球疫情的變化趨勢,我繪製了一張疫情變化地圖,完整代碼共 230 行,須要的朋友在公衆號回覆關鍵字 疫情地圖 便可。廢話很少說,先上圖html
下面就來重點介紹下上面這張圖的繪製過程,主要分爲如下三個步驟:python
數據收集mongodb
數據處理編程
畫圖json
下面一個一個來講。
數據收集api
這是萬里長城的第一步,俗話說「巧婦難爲無米之炊」,既然是變化圖,固然須要每一個國家、天天的現有確診病例數。好在如今各大網站都有疫情相關的專題頁,咱們能夠直接抓數據。以網易爲例數組
咱們選擇 XHR,從新刷新下網頁能夠看到有幾個接口,其中 list-total 接口是獲取當前全部有疫情的國家,以及對應的國家id。另外,咱們看到還有一個 list-by-area-code 接口,它是獲取每一個國家歷史上天天的疫情數據,請求這個接口須要帶 areaCode 參數,這個參數就是咱們剛剛說的國家id。因此對咱們來講這兩個接口是最重要的。下面咱們就看看請求 list-total 接口的代碼markdown
def get_and_save_all_countries(): """ 獲取全部的國家名以及對應的id,保存爲文件 """ url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=317452696323' list_total_req = requests.get(url, headers=headers) if list_total_req.status_code == 200: area_tree = list_total_req.json()['data']['areaTree'] area_dict = {} for area in area_tree: country_id = area['id'] name = area['name'] area_dict[country_id] = name area_json = json.dumps(area_dict, ensure_ascii=False) # ensure_ascii=False 防止json編碼後中文編程\u開頭的字符 write_file('./config/countries_id2name.json', area_json)複製代碼
這裏將請求下來的數據臨時存放在文件裏。有了全部的疫情國家的id,咱們就能夠請求 list-by-area-code 接口來獲取每一個國家的疫情數據了。代碼與上面的相似,不一樣的是將請求結果存在了 mongodb 而不是文件,目的是爲了方便增刪改查。固然爲了你們方便使用,我將mongodb中的數據導入了文件 counties_daily.json 中,你們能夠在源碼根目錄找到它。數據結構
這一步的處理主要是爲第三步畫圖作準備的。由於咱們畫圖用的是pyecharts框架,它繪製世界地圖須要輸入的國家名是英文的,而咱們收集的國家名是中文的,因此要將中文國家名對應到英文國家名。最終的效果以下app
網上能找到這樣的對應關係,但想要用起來還須要解決兩個問題。第一,兩邊中文名統一,好比:咱們收集的國家名是中非共和國,而對應關係裏是中非,那仍是對應不上。第二,須要本身增長映射關係,網上找的通常都不全,咱們須要根據收集的數據自行增長。通過上面兩個步驟處理後,咱們就能夠將大部分國家名對應到pyechars能識別的英文名了。相關代碼以下
def get_cy_properties(): # 獲取配置文件信息 countries_id2name = read_file('./config/countries_id2name.json') cy_id2name_dict = json.loads(countries_id2name) cy_ch2en = {v: k for k, v in countries_dict.items()} # 調整國家的名字與配置文件一致 cy_id2name_dict['879'] = '波斯尼亞和黑塞哥維那' cy_id2name_dict['8102'] = '多哥' cy_id2name_dict['8143'] = '剛果民主共和國' cy_id2name_dict['95983'] = '剛果' cy_id2name_dict['8144'] = '中非' cy_id2name_dict['95000011'] = '多米尼加' cy_props = {} for key in cy_id2name_dict: cy_name = cy_id2name_dict[key] if cy_name in cy_ch2en: cy_props[cy_name] = {} cy_props[cy_name]['id'] = key cy_props[cy_name]['en_name'] = cy_ch2en[cy_name] return cy_props複製代碼
這一步涉及到兩個核心過程——構造數據結構和畫圖。首先,我構造了3個數據結構,分別是date_list、cy_name_list 和 ncov_data。date_list存放的是日期列表,由於咱們畫動圖,因此須要一段時間;cy_name_list 存放收集的全部國家列表(英文名);ncov_data是一個字典,key是日期,value是數組,存放各個國家當天的確診病例數。生成這三個數據結構的代碼以下
def parse_ncov_data(start_date, end_date, records): if not records: return date_list = get_date_range(start_date, end_date) cy_name_list = [] res = {} # 獲取各國天天現有確認病例 for i, record in enumerate(records): cy_name = record['cy_en_name'] cy_name_list.append(cy_name) # 解析天天數據並計算現有確認病例 existing_case_dict = {} for ncov_daily in record['data']['list']: date_str = ncov_daily['date'] confirm = ncov_daily['total']['confirm'] # 累計確診 heal = ncov_daily['total']['heal'] # 累計確診 dead = ncov_daily['total']['dead'] # 累計死亡 existing_case = confirm - heal - dead existing_case_dict[date_str] = existing_case last_existing_case = 0 # 將天天確診病例數合併到res中 for date_str in date_list: if date_str not in res: # 初始化 res[date_str] = [] existing_case = existing_case_dict.get(date_str) if existing_case is None: existing_case = last_existing_case res[date_str].append(existing_case) last_existing_case = existing_case return date_list, cy_name_list, res複製代碼
參數 records 是一個數組,數組每一個元素表明一個國家,內容即是咱們在第一步請求 list-by-area-code 接口的數據。最後,用 pyecharts 來畫圖,直接上代碼
def render_map(date_list, cy_name_list, ncov_data): tl = Timeline() # 建立時間線輪播多圖,可讓圖形按照輸入的時間動起來 # is_auto_play:自動播放 # play_interval:播放時間間隔,單位:毫秒 # is_loop_play:是否循環播放 tl.add_schema(is_auto_play=True, play_interval=50, is_loop_play=False) for date_str in date_list: # 遍歷時間列表 map0 = ( Map() # 建立地圖圖表 # 將國家名 cy_name_list 以及各國當天確診病例 ncov_data[date_str] 加入地圖中 .add("全球疫情趨勢", [list(z) for z in zip(cy_name_list, ncov_data[date_str])], "world", is_map_symbol_show=False) .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) # 不顯示國家名 .set_global_opts( title_opts=opts.TitleOpts(title="%s日" % date_str), # 圖表標題 visualmap_opts=opts.VisualMapOpts(max_=80), # 當確診病例大於80 ,地圖顏色是紅色 ) ) tl.add(map0, "%s" % date_str) # 將當天的地圖狀態加入時間線中 tl.render() # 生成最終輪播多圖,會在當前目錄建立 render.html 文件複製代碼
代碼里加了註釋,這裏就再也不贅述了。
運行 render_map 函數會在當前目錄生成 render.html 文件,打開後便自動播放疫情變化趨勢,如文章開頭 gif。另外,有些朋友可能會問,能不能直接輸出 gif。這一點我也嘗試過,百度、谷歌、GitHub上的教程基本上都試了一遍,比較遺憾沒有找到靠譜的方法。因此勸你們仍是放棄這條路,曲線救國,錄製一個視頻轉成 gif 便可,方便快捷。畢竟人生苦短,Python 爲咱們節省下的時間不能再被這些無謂的坑再填回去。這樣整個過程就介紹完了,雖然思路不復雜,但局部細節上仍是須要花一些時間處理的。完整代碼共 230 行,須要的朋友在公衆號回覆關鍵字 疫情地圖 便可。
最近國內某些地方出現了反彈的跡象,但願你們不管是在工做仍是生活上都能繼續保持警戒。但願此次疫情早點過去,等待全球地圖變白的那一天。
歡迎公衆號「渡碼」,輸出別地兒看不到的乾貨。