Python实战:抓取肺炎疫情实时数据,画2019-nCoV疫情地图

本篇文章根据根据文章进行实时调整而来,因为原文章所爬取的数据格式来源有所改变,故修改如下。

1. 前言

本篇文章的内容是用python画武汉肺炎疫情地图。先前并没有关注武汉肺炎的具体数据,也没有画过类似的数据分布图。于是利用现成的腾讯大数据疫情地图,专门研究了一下,遂成此文。

2. 数据下载

数据来源是腾讯新型冠状病毒肺炎疫情实时追踪
在这里插入图片描述
有了网址怎么抓数据呢?这里。我习惯用Google浏览器,下面的讲解就以Google为例(其他浏览器基本类似)。

打开菜单,右键:检查,在下面菜单中选择"network":
在这里插入图片描述
刷新页面,我们很快就能发现,应答类型为json格式的这个请求,最有可能包含我们需要的数据了:
地址是:https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5&callback=jQuery34104747921406451956_1580809278602&_=1580809278603
在这里插入图片描述
我们打开网址看到数据格式如下:发现数据中既包含国家、省、各个城市,又包括感染人数和死亡人数等等,结构比较清晰,易于提取。
在这里插入图片描述
深入分析,我们就得到了url地址、请求方法、参数、应答格式等信息。查询参数中,callback是回调函数名,我们可以尝试置空,_应该是以毫秒为单位的当前时间戳。有了这些信息,分分钟就可以抓到数据了。我们先在IDLE中以交互方式抓一下看看效果:
我们发现数据格式有些混乱,我们希望的是整理成一个规范的字典格式,所以我们接下来对数据进行一些清洗。

import time, json, requests
#url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_area_counts&callback=&_=%d'%int(time.time()*1000)
url='https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5&&callback=&_=%d'%int(time.time()*1000)
data = json.loads(requests.get(url=url).json()['data'])
print(len(data))
data

在这里插入图片描述
经过下面的数据清洗,我们将所有的字典组成一个大的表格形式。其中每个字典中包含这样几个数据:国家、省、城市、总共确认感染数、怀疑感染数、总共死亡人数、治愈人数、今天确认感染人数、今天确认怀疑感染人数、今天死亡人数和今天治愈人数。

lis=[]

#pron_num=[i for i in range(len(data['areaTree'][0]['children']))]
#city_num=[j for j in range(len(data['areaTree'][0]['children'][i]['children']))]
for m in range(len(data['areaTree'][0]['children'])):
    for n in range(len(data['areaTree'][0]['children'][m]['children'])):
        info={}
        info['country']=data['areaTree'][0]['name']#国家
        info['pronvice']=data['areaTree'][0]['children'][m]['name']#省份  
        info['city']=data['areaTree'][0]['children'][m]['children'][n]['name']#城市   len(data['areaTree'][0]['children'][0]['children'])
        info['total_confirm']=data['areaTree'][0]['children'][m]['children'][n]['total']['confirm']
        info['total_suspect']=data['areaTree'][0]['children'][m]['children'][n]['total']['suspect']
        info['total_dead']=data['areaTree'][0]['children'][m]['children'][n]['total']['dead']
        info['total_heal']=data['areaTree'][0]['children'][m]['children'][n]['total']['heal']
        info['today_confirm']=data['areaTree'][0]['children'][m]['children'][n]['today']['confirm']
        info['today_suspect']=data['areaTree'][0]['children'][m]['children'][n]['today']['suspect']
        info['today_dead']=data['areaTree'][0]['children'][m]['children'][n]['today']['dead']
        info['today_heal']=data['areaTree'][0]['children'][m]['children'][n]['today']['heal']
        lis.append(info)
lis

在这里插入图片描述
只要几行代码,就可以抓到数据了。怎么样,是不是超级简单?

3. 数据处理

以省为单位画疫情图,我们只需要统计同属一个省的所有地市的确诊数据即可。最终的数据抓取代码如下:


def catch_distribution():
    """抓取行政区域确诊分布数据"""
    
    data = {}
    for item in lis:
        
        if item['pronvice'] not in data:
            data.update({item['pronvice']:0})
        data[item['pronvice']] += int(item['total_confirm'])
    
    return data

4. 数据可视化

数据可视化,我习惯使用matplotlib模块。matplotlib有很多扩展工具包(toolkits),比如,画3D需要mplot3d工具包,画地图的话,则需要basemap工具包,以及处理地图投影的pyproj模块。另外画海陆分界线、国界线、行政分界线等还需要shape数据。所需模块请自行安装,shape文件可以从这里下载,绘图用到的矢量字库可以从自己的电脑上随便找一个(我用的是simsun.ttf)。

5.代码

除了疫情地图,还包括了全国每日武汉肺炎确诊数据的下载和可视化。

import time
import json
import requests
from datetime import datetime
import numpy as np
import matplotlib
import matplotlib.figure
from matplotlib.font_manager import FontProperties
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import matplotlib.dates as mdates


plt.rcParams['font.sans-serif'] = ['FangSong']  # 设置默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像时'-'显示为方块的问题

def catch_daily():
    """抓取每日确诊和死亡数据"""
    
    url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_cn_day_counts&callback=&_=%d'%int(time.time()*1000)
    data = json.loads(requests.get(url=url).json()['data'])
    data.sort(key=lambda x:x['date'])
    
    date_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'))
        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, confirm_list, suspect_list, dead_list, heal_list



def catch_distribution():
    """抓取行政区域确诊分布数据"""
    
    data = {}
    for item in lis:
        
        if item['pronvice'] not in data:
            data.update({item['pronvice']:0})
        data[item['pronvice']] += int(item['total_confirm'])
    
    return data


def plot_daily():
    """绘制每日确诊和死亡数据"""
    
    date_list, confirm_list, suspect_list, dead_list, heal_list = catch_daily() # 获取数据
    
    plt.figure('2019-nCoV疫情统计图表', facecolor='#f4f4f4', figsize=(10, 8))
    plt.title('2019-nCoV疫情曲线', fontsize=20)
    
    plt.plot(date_list, confirm_list, label='确诊')
    plt.plot(date_list, suspect_list, label='疑似')
    plt.plot(date_list, dead_list, label='死亡')
    plt.plot(date_list, heal_list, label='治愈')
    
    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m-%d')) # 格式化时间轴标注
    plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
    plt.grid(linestyle=':') # 显示网格
    plt.legend(loc='best') # 显示图例
    #plt.savefig('2019-nCoV疫情曲线.png') # 保存为文件
    plt.show()

    
def plot_distribution():
    """绘制行政区域确诊分布数据"""
    
    data = catch_distribution()
    
    font = FontProperties(fname='china-shapefiles/simsun.ttf', size=14)
    lat_min = 0
    lat_max = 60
    lon_min = 70
    lon_max = 140
    
    handles = [
            matplotlib.patches.Patch(color='#ffaa85', alpha=1, linewidth=0),
            matplotlib.patches.Patch(color='#ff7b69', alpha=1, linewidth=0),
            matplotlib.patches.Patch(color='#bf2121', alpha=1, linewidth=0),
            matplotlib.patches.Patch(color='#7f1818', alpha=1, linewidth=0),
]
    labels = [ '1-9人', '10-99人', '100-999人', '>1000人']
    
    fig = matplotlib.figure.Figure()
    fig.set_size_inches(10, 8) # 设置绘图板尺寸
    axes = fig.add_axes((0.1, 0.12, 0.8, 0.8)) # rect = l,b,w,h
    
    m = Basemap(llcrnrlon=lon_min, urcrnrlon=lon_max, llcrnrlat=lat_min, urcrnrlat=lat_max, resolution='l', ax=axes)
    #m = Basemap(projection='ortho', lat_0=30, lon_0=105, resolution='l', ax=axes)
    #m = Basemap(projection='ortho', lat_0=30, lon_0=105, resolution='l', ax=axes)


    m.readshapefile('./china-shapefiles/china', 'province', drawbounds=True)
    m.readshapefile('./china-shapefiles/china_nine_dotted_line', 'section', drawbounds=True)
    m.drawcoastlines(color='black') # 洲际线
    m.drawcountries(color='black')  # 国界线
    m.drawparallels(np.arange(lat_min,lat_max,10), labels=[1,0,0,0]) #画经度线
    m.drawmeridians(np.arange(lon_min,lon_max,10), labels=[0,0,0,1]) #画纬度线
    
    for info, shape in zip(m.province_info, m.province):
        pname = info['OWNER'].strip('\x00')
        fcname = info['FCNAME'].strip('\x00')
        if pname != fcname: # 不绘制海岛
            continue
        
        for key in data.keys():
            if key in pname:
                if data[key] == 0:
                    color = '#f0f0f0'
                    poly = Polygon(shape, facecolor=color, edgecolor=color)
                    axes.add_patch(poly)
                elif data[key] < 10:
                    color = '#ffaa85'
                    poly = Polygon(shape, facecolor=color, edgecolor=color)
                    axes.add_patch(poly)
                elif data[key] <100:
                    color = '#ff7b69'
                    poly = Polygon(shape, facecolor=color, edgecolor=color)
                    axes.add_patch(poly)
                elif  data[key] < 1000:
                    color = '#bf2121'
                    poly = Polygon(shape, facecolor=color, edgecolor=color)
                    axes.add_patch(poly)
                else:
                    color = '#7f1818'
                    poly = Polygon(shape, facecolor=color, edgecolor=color)
                    axes.add_patch(poly)
                break
                

    
    axes.legend(handles, labels, bbox_to_anchor=(0.5, -0.11), loc='lower center', ncol=4, prop=font)
    axes.set_title("2019-nCoV疫情地图", fontproperties=font)
    FigureCanvasAgg(fig)
    fig.savefig('2019-nCoV疫情地图.png')
    fig.set_visible(b=True)

if __name__ == '__main__':
    plot_daily()
    plot_distribution()

2019-nCoV疫情曲线:
在这里插入图片描述
2019-nCoV疫情地图:
在这里插入图片描述
上图为圆柱投影,这也是basemap默认的投影模式,我们还可以换用其他投影模式,比如兰勃托等角投影,只需要将之前代码改为:

m = Basemap(projection='lcc', width=5000000, height=5000000, lat_0=36, lon_0=102, resolution='l', ax=axes)

兰勃托投影效果如下:
在这里插入图片描述
还可以使用正射投影:

扫描二维码关注公众号,回复: 8988490 查看本文章
m = Basemap(projection='ortho', lat_0=30, lon_0=105, resolution='l', ax=axes)

正射投影效果如下:
在这里插入图片描述
可能会遇到的几个困难:
1.SimSun.ttf下载地址
2.china-shapefiles下载地址 使用github进行下载会带来一个问题就是下载速度过慢,关于如何解决这个问题,可以使用码云,详细教程可以看文章,然后下载速度就非常快了。
3.如何下载Basemap文件,这个是需要到网址进行下载即可,不过下载的时候需要注意要找到本地python对应的版本,否则无效。如果不会安装,可以参考wheel如何安装

参考文章:

1.https://blog.csdn.net/xufive/article/details/104093197

发布了127 篇原创文章 · 获赞 21 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_35770067/article/details/104172396