Python 繪製全球疫情地圖

極客挖掘機發表於2020-05-26

國內疫情得到控制後,我就沒怎麼再關心過疫情,最近看到一條新聞,全球疫情累計確診人數已經突破 500w 大關,看到這個數字我還是有點吃驚的。

思來想去,還是寫一篇全球疫情的分析的文章,本文包括網路爬蟲、全球疫情地圖繪製等方面。

網路爬蟲

我之前有分享過疫情資料的來源,用的是 AKShare 的資料來源,好用是真好用,就是網路太慢了, AKShare 的資料很多是來源於 GitHub ,我的網路訪問太平洋彼岸的資料還是有點力不從心。

這次我換了新的資料來源,來源騰訊新聞的實時資料,站點連結如下:

本來我以為需要解析頁面元素,才能獲取到資料,但是等我分析了 network 以後發現,竟然可以直接找到資料介面,這大大的方便了我們資料抓取。

獲取全球疫情資料介面如下:

https://api.inews.qq.com/newsqa/v1/automation/foreign/country/ranklist

把這個介面放在 PostMan 裡面模擬訪問一下:

毫無反爬手段,header 神馬的都不需要配置,直接訪問就能拿到資料,到這裡,我們可以開始寫爬蟲的程式碼了,最終程式碼如下:

import requests
from datetime import datetime

def catch_data():
    """
    抓取當前實時資料,並返回 國家、大洲、確診、疑似、死亡、治癒 列表
    :return:
    """
    url = 'https://api.inews.qq.com/newsqa/v1/automation/foreign/country/ranklist'
    data = requests.post(url).json()['data']

    date_list = list()  # 日期
    name_list = list() # 國家
    continent_list = list() # 大洲
    confirm_list = list()  # 確診
    suspect_list = list()  # 疑似
    dead_list = list()  # 死亡
    heal_list = list()  # 治癒

    for item in data:
        month, day = item['date'].split('.')
        date_list.append(datetime.strptime('2020-%s-%s' % (month, day), '%Y-%m-%d'))
        name_list.append(item['name'])
        continent_list.append(item['continent'])
        confirm_list.append(int(item['confirm']))
        suspect_list.append(int(item['suspect']))
        dead_list.append(int(item['dead']))
        heal_list.append(int(item['heal']))

    return date_list, name_list, continent_list, confirm_list, suspect_list, dead_list, heal_list


def save_csv():
    """
    將資料存入 csv 檔案
    :return:
    """
    date_list, name_list, continent_list, confirm_list, suspect_list, dead_list, heal_list = catch_data()
    fw = open('2019-nCoV.csv', 'w', encoding='utf-8')
    fw.write('date,name,continent,confirm,suspect,dead,heal\n')

    i = 0
    while i < len(date_list):
        date = str(date_list[i].strftime("%Y-%m-%d"))
        fw.write(date + ',' + str(name_list[i]) + ',' + str(continent_list[i]) + ',' + str(confirm_list[i]) + ',' + str(suspect_list[i]) + ',' + str(dead_list[i]) + ',' + str(heal_list[i]) + '\n')
        i = i + 1
    else:
        print("csv 寫入完成")
        fw.close()


if __name__ == '__main__':
    save_csv()

最終得到的 csv 檔案是這樣的:

全球疫情地圖

前端或網站開發的朋友應該都使用過強大的 Echarts 外掛。 ECharts 是一個純 Javascript 的圖表庫,可以流暢的執行在 PC 和移動裝置上,相容當前絕大部分瀏覽器,底層依賴輕量級的 Canvas 類庫 ZRender ,提供直觀、生動、可互動、可高度個性化定製的資料視覺化圖表。 ECharts 提供了常規的折線圖、柱狀圖、散點圖、餅圖、K線圖,用於統計的盒形圖,用於地理資料視覺化的地圖、熱力圖、線圖,用於關係資料視覺化的關係圖、treemap,多維資料視覺化的平行座標,還有用於 BI 的漏斗圖、儀表盤,並且支援圖與圖之間的混搭。

既然 Echarts 如此強大, Python 肯定有相應的第三方擴充套件包支援,它就是我們接下來繪製世界地圖要用到的 PyEcharts 。 PyEcharts 是一個用於生成 Echarts 圖表的類庫,即 Echarts 與 Python 的對接。

安裝語句如下:

pip install pyecharts

PyEcharts 安裝完成後我們就可以開始寫接下來的程式碼了,如下:

from pyecharts import options as opts
from pyecharts.charts import Map
import pandas as pd
import namemap

def read_country_code():
    """
    獲取國家中英文字典
    :return:
    """
    country_dict = {}
    for key, val in namemap.nameMap.items():  # 將 nameMap 列表裡面鍵值互換
        country_dict[val] = key
    return country_dict

def read_csv():
    """
    讀取資料,返回國家英文名稱列表和累計確診數列表
    :return:
    """
    country_dict = read_country_code()
    data = pd.read_csv("2019-nCoV.csv", index_col=False)

    countrys_names = list()
    confirmed_count = list()

    for x in range(len(data.index)):
        if data['name'].iloc[x] in country_dict.keys():
            countrys_names.append(country_dict[data['name'].iloc[x]])
            confirmed_count.append(data['confirm'].iloc[x])
        else:
            print(data['name'].iloc[x])

    return countrys_names, confirmed_count


def draw_map():
    """
    繪製世界地圖
    遇到一個很神奇的問題:
    兩個列表必須寫死資料地圖才會渲染資料,如果資料是從方法中獲得,則地圖不渲染資料
    :return:
    """

    # countrys_names, confirmed_count = read_csv()
    # print(countrys_names)
    # print(confirmed_count)

    countrys_names = ['United States', 'Brazil', 'Russia'...]

    confirmed_count = [1666828, 347398, 335882...]


    c = (
        Map()
        .add(
            "確診人數",
            [list(z) for z in zip(countrys_names, confirmed_count)],
            is_map_symbol_show=False,
            maptype="world",
            label_opts=opts.LabelOpts(is_show=False),
            itemstyle_opts=opts.ItemStyleOpts(color="rgb(49,60,72)")
        )
        .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
        .set_global_opts(
            title_opts=opts.TitleOpts(title="全球 2019-nCoV 地圖"),
            visualmap_opts=opts.VisualMapOpts(max_=1700000),
        )
        .render("map_world.html")
    )


if __name__ == '__main__':
    draw_map()

最終結果如下:

在繪製全球疫情地圖的時候,我們最終使用的國家的名稱是英文的,所以需要用到一箇中英文國家名稱對照字典,這個字典我找到了兩個版本,一個是 Python 格式的檔案 namemap.py ,還有一個是 json 格式的檔案 country-code.json , 使用這兩個檔案中的任意一個將我們在前面獲取到的資料中的中文國家名稱轉換為英文。這兩個檔案我都會提交到程式碼倉庫,有需要的同學可以在公眾號裡回覆關鍵字獲取。

另外,在最後繪製地圖的時候遇到了一個很奇葩的問題,國家名稱列表和累計確診人數列表如果是從前面的方法中獲取到的,在最後渲染成 map_world.html 的時候,將不會渲染數字,所有的數字都是 null ,但是如果這兩個列表 copy 出來,寫死在程式碼中,就可以成功的渲染,有清楚這個問題的朋友可以在留言中解答一下,萬分感激。

需要原始碼的同學可以在公眾號後臺回覆「全球疫情」獲取。

相關文章