前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Python爬取COVID-19疫情数据

使用Python爬取COVID-19疫情数据

原创
作者头像
数据酷客
修改2020-04-27 14:14:17
9K3
修改2020-04-27 14:14:17
举报
视频内容

新型冠状病毒感染的肺炎疫情爆发后,对人们的生活产生很大的影响。当前感染人数依然在不断变化。每天国家卫健委和各大新闻媒体都会公布疫情的数据,包括累计确诊人数、现有确诊人数等。

本案例使用Python开发网络爬虫,对新冠肺炎的疫情数据(包括当日的实时数据和历史数据)进行采集。

1. 选择数据源

自新冠肺炎(covid-19)疫情爆发以来,这场疫情几乎影响了每个人的生活,为了对疫情做数据分析,需要采集疫情的数据,本篇案例就基于python爬虫进行数据采集。

首先我们需要找到合适的数据源,卫健委和各媒体每天都会报道新冠肺炎的疫情数据,如下图所示,因此我们可以考虑将这些网页作为数据源。

以北京市和湖南省为例查看卫健委的数据,结果发现,两地卫健委的数据各式并不统一,北京市卫健委数据为文本,而湖南省卫健委数据为图片,并且由于各卫健委的网络地址还需要再花时间去寻找,因此卫健委的数据并不便于采集。

  • 北京市卫健委疫情通报地址:http://wjw.beijing.gov.cn/wjwh/ztzl/xxgzbd/gzbdyqtb/index_1.html
  • 湖南省卫健委疫情通报地址:http://wjw.hunan.gov.cn/wjw/qwfb/yqfkgz_list.html

我们考虑将新闻媒体的播报平台作为数据源,以网易的疫情播报平台为例,如下图所示可以看到它的数据内容非常丰富,不仅包括国内的数据还包括国外的数据,且作为大平台,公信度也比较高。因此我们选择网易的疫情实时动态播报平台作为数据源,其地址如下:

https://wp.m.163.com/163/page/news/virus_report/index.html?_nw_=1&_anw_=1

我们基于网易的实时播报平台寻找数据,由于它是一个实时的动态平台,因此数据一般在Network标签下可以找到,以Chrome浏览器为例展示寻找数据步骤:

  • 访问网易实时疫情播报平台https://wp.m.163.com/163/page/news/virus_report/index.html?_nw_=1&_anw_=1
  • 在页面任意位置右键点击检查
  • 进入Network标签下的XHR,此时可能会提示刷新,按下“Ctrl+R”即可

在网页的检查页面内,左下角红框所示的部分是我们找到的数据源,基于这些地址进行爬取,我们获取数据的目标如下:

2. 初步探索

通过比对,我们发现在第二个地址中存放着关于疫情的数据,因此我们先对这个地址进行爬虫。

接下来找到其地址,点击headers后进行查看,在url中?后边为参数,可以不用设置,因此我们需要请求的地址为:https://c.m.163.com/ug/api/wuhan/app/data/list-total,并且可以看到请求方法为get,同时查看自己浏览器的user-agent,使用requests请求时,设置user-agent伪装浏览器。

下来开始请求,首先导入使用的包,使用request进行网页请求,使用pandas保存数据。

代码语言:javascript
复制
import requests
import pandas as pd
import time 
pd.set_option('max_rows',500)

设置请求头,伪装为浏览器

代码语言:javascript
复制
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
}

发起请求,将找到的第一个数据源作为请求目标。

代码语言:javascript
复制
url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-total'   # 定义要访问的地址
r = requests.get(url, headers=headers)  # 使用requests发起请求
代码语言:javascript
复制
print(r.status_code)  # 查看请求状态

200

代码语言:javascript
复制
print(type(r.text))
print(len(r.text))

<class 'str'> 301443

可以看到返回后的内容是一个几十万长度的字符串,由于字符串格式不方便进行分析,并且在网页预览中发现数据为类似字典的json格式,所以我们将其转为json格式。

代码语言:javascript
复制
import json
data_json = json.loads(r.text)
data_json.keys()

dict_keys(['reqId', 'code', 'msg', 'data', 'timestamp'])

可以看出在data中存放着我们需要的数据,因此我们取出数据。

代码语言:javascript
复制
data = data_json['data'] # 取出json中的数据
data.keys()

dict_keys(['chinaTotal', 'chinaDayList', 'lastUpdateTime', 'areaTree'])

数据中总共有四个键,每个键存储着不同的内容:

接下来我们分别获取实时数据和历史数据。

键名称

数据内容

chinaTotal

全国当日数据

chinaDayList

全国历史数据

lastUpdateTime

更新时间

areaTree

世界各地实时数据

3. 实时数据爬取

3.1 全国各省实时数据爬取

首先我们抓取全国各省的实时数据,在areaTree键值对中,存放着世界各地的实时数据,areaTree是一个列表,每一个元素都是一个国家的数据,每一个元素的children是各国家省份的数据。 我们首先找到中国各省的实时数据,如下图所示。

代码语言:javascript
复制
data_province = data['areaTree'][2]['children']  # 取出中国各省的实时数据
代码语言:javascript
复制
type(data_province)

list

data_province是列表格式,每个元素是一个省的实时数据,并且为字典格式,每个省的键名称全部相同。

代码语言:javascript
复制
data_province[0].keys() # 查看每个省键名称

dict_keys(['today', 'total', 'extData', 'name', 'id', 'lastUpdateTime', 'children'])

各省数据键值含义如下:

键名称

数据内容

today

各省当日数据

total

各省当日累计数据

extData

无任何数据

name

各省名称

id

各省行政编号

lastUpdateTime

更新时间

children

各省下一级数据

代码语言:javascript
复制
for i in range(len(data_province)): # 遍历查看各省名称、更新时间
    print(data_province[i]['name'],data_province[i]['lastUpdateTime'])
    if i == 5:
        break

湖北 2020-03-25 08:15:00 广东 2020-03-25 08:44:58 河南 2020-03-25 08:23:28 浙江 2020-03-25 09:01:13 湖南 2020-03-25 08:25:33 安徽 2020-03-25 08:38:12

用DataFrame以字典格式生成数据的例子,传入一个列表,列表每一个元素都是字典。

代码语言:javascript
复制
test_dict = [{'a':1,
              'b':2,
              'c':3,},
             
             {'a':111,
              'b':222}]
pd.DataFrame(test_dict)
代码语言:javascript
复制
pd.DataFrame(data_province).head() # 直接生成数据效果并不理想

不能直接生成DataFrame是因为数据中嵌套着字典,例如湖北省数据如下:标红线表示带有嵌套字典,篮筐内没有嵌套字典。

逐个拆解数据,我们已经了解children为下一地级数据,我们只分析到省单位,因此各省的children数据不采集;extData为空值,也不采集,最后具体采集方法如下:

不需要采集的数据:children、extData。 需要采集的数据:由于数据中today和total嵌套着字典,因此不能直接获取,对于id、lastUpdateTime、name、可以直接取出为一个数据,today为一个数据,total为一个数据,最后三个数据合并为一个数据。

代码语言:javascript
复制
# 获取id、lastUpdateTime、name
info = pd.DataFrame(data_province)[['id','lastUpdateTime','name']]
info.head()

列表推导式例子

代码语言:javascript
复制
l1 = [1,1,1,2,2,2]
代码语言:javascript
复制
[i+1 for i in l1 ]

[2, 2, 2, 3, 3, 3]

代码语言:javascript
复制
# 获取today中的数据
today_data = pd.DataFrame([province['today'] for province in data_province ]) 
today_data.head()
代码语言:javascript
复制
['today_'+i for i in today_data.columns]

['today_confirm', 'today_dead', 'today_heal', 'today_severe', 'today_storeConfirm', 'today_suspect']

代码语言:javascript
复制
today_data.columns = ['today_'+i for i in today_data.columns] # 由于today中键名和total键名相同,因此需要修改列名称
代码语言:javascript
复制
today_data.head()
代码语言:javascript
复制
# 获取total中的数据
total_data = pd.DataFrame([province['total'] for province in data_province ])
total_data.columns = ['total_'+i for i in total_data.columns]
total_data.head()
代码语言:javascript
复制
pd.concat([info,total_data,today_data],axis=1).head() # 将三个数据合并
代码语言:javascript
复制
# 将提取数据的方法封装为函数
def get_data(data,info_list):
    info = pd.DataFrame(data)[info_list] # 主要信息
    
    today_data = pd.DataFrame([i['today'] for i in data ]) # 生成today的数据
    today_data.columns = ['today_'+i for i in today_data.columns] # 修改列名
    
    total_data = pd.DataFrame([i['total'] for i in data ]) # 生成total的数据
    total_data.columns = ['total_'+i for i in total_data.columns] # 修改列名
    
    return pd.concat([info,total_data,today_data],axis=1) # info、today和total横向合并最终得到汇总的数据
代码语言:javascript
复制
today_province = get_data(data_province,['id','lastUpdateTime','name'])
today_province.head()
代码语言:javascript
复制
def save_data(data,name): # 定义保存数据方法
    file_name = name+'_'+time.strftime('%Y_%m_%d',time.localtime(time.time()))+'.csv'
    data.to_csv(file_name,index=None,encoding='utf_8_sig')
    print(file_name+' 保存成功!')
代码语言:javascript
复制
time.strftime('%Y_%m_%d',time.localtime(time.time()))

'2020_03_25'

代码语言:javascript
复制
save_data(today_province,'today_province')

today_province_2020_03_25.csv 保存成功!

3.2 世界各国实时数据爬取

之前已经了解到在json数据data中的areaTree是列表格式,每个元素都是一个国家的实时数据,每个元素的children是各国家省份的数据,现在我们提取世界各国实时数据。

代码语言:javascript
复制
areaTree = data['areaTree'] # 取出areaTree
代码语言:javascript
复制
areaTree[0] # 查看第一个国家的数据

{'children': [], 'extData': {'confirmNote': None, 'deadNote': None, 'healNote': None, 'incrConfirmNote': None, 'incrSevereNote': None, 'suspectNote': None}, 'id': '9577772', 'lastUpdateTime': '2020-03-25 00:00:31', 'name': '突尼斯', 'today': {'confirm': None, 'dead': None, 'heal': None, 'severe': None, 'storeConfirm': None, 'suspect': None}, 'total': {'confirm': 114, 'dead': 3, 'heal': 0, 'severe': 0, 'suspect': 0}}

areaTre中每个键值的含义:

代码语言:javascript
复制
for i in range(len(areaTree)):  # 查看各国家名称和更新时间
    print(areaTree[i]['name'],areaTree[i]['lastUpdateTime'])
    if i == 5:
        break

突尼斯 2020-03-25 00:00:31 塞尔维亚 2020-03-25 03:57:40 中国 2020-03-25 09:52:07 日本 2020-03-24 15:26:25 泰国 2020-03-25 00:00:30 新加坡 2020-03-25 00:00:30

areaTree中提取每个国家的实时数据。

代码语言:javascript
复制
today_world = get_data(areaTree,['id','lastUpdateTime','name'])
today_world.head()
代码语言:javascript
复制
save_data(today_world,'today_world')

today_world_2020_03_25.csv 保存成功!

4.历史数据爬取

4.1 全国历史数据爬取

在数据data中,总共有四个键,其中chinaDayList存放着中国的历史数据,我们将其取出。

代码语言:javascript
复制
data.keys()

dict_keys(['chinaTotal', 'chinaDayList', 'lastUpdateTime', 'areaTree'])

代码语言:javascript
复制
chinaDayList = data['chinaDayList'] # 取出chinaDayList

chinaDayList中键值含义:

键名称

数据内容

date

日期

today

当日数据

total

累计数据

lastUpdateTime

更新时间

代码语言:javascript
复制
type(chinaDayList) # 查看chinaDayList的格式

list

代码语言:javascript
复制
chinaDayList[0]

{'date': '2020-01-20', 'lastUpdateTime': None, 'today': {'confirm': 291, 'dead': 6, 'heal': 25, 'severe': 0, 'storeConfirm': None, 'suspect': 27}, 'total': {'confirm': 291, 'dead': 6, 'heal': 25, 'severe': 0, 'suspect': 54}}

可以看到todaytotal中也嵌套着字典,因此直接使用定义好的方法从chinaDayList中提取全国历史数据。

代码语言:javascript
复制
alltime_China = get_data(chinaDayList,['date','lastUpdateTime'])
alltime_China.head()
代码语言:javascript
复制
save_data(alltime_China,'alltime_China')

alltime_China_2020_03_25.csv 保存成功!

4.2 全国各省历史数据爬取

找到第二个数据地址https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode=420000 ,在页面对比发现是湖北省的历史数据。

先以其中一个省为例,先尝试获取其历史数据,其他的省可以使用同样的方法。

代码语言:javascript
复制
url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode=420000' # 定义数据地址
r = requests.get(url, headers=headers) # 进行请求
data_json = json.loads(r.text) # 获取json数据
代码语言:javascript
复制
data_json.keys()

dict_keys(['reqId', 'code', 'msg', 'data', 'timestamp'])

list中存放着数据。

代码语言:javascript
复制
data_json['data']['list'][0]

{'date': '2020-01-20', 'lastUpdateTime': None, 'today': {'confirm': 270, 'dead': 6, 'heal': 25, 'severe': None, 'storeConfirm': None, 'suspect': 0}, 'total': {'confirm': 270, 'dead': 6, 'heal': 25, 'severe': 0, 'suspect': 0}}

可以看出一个省一天的的数据格式如上,这和之前的数据结构一样,因此可以使用之前的方法得到数据。

代码语言:javascript
复制
data_test = get_data(data_json['data']['list'],['date'])
data_test['name'] = '湖北省'
data_test.head()

通过上述方法得到了湖北省的历史数据,想要得到每个省的历史数据怎么做呢?

在湖北省历史数据的地址中,我们发现参数aeraCode=420000,而这刚好和全国各省实时数据today_province中的id对应.

代码语言:javascript
复制
today_province[['id','name']].head()

为了进一步确认,在百度上查找全国各省的行政代码,结果发现和数据today_province中的id这一列一致,因此id这一列就是各省的行政代码。

因此为了得到每个省的历史数据,我们只需要将各省的行政代码作为参数传入这个地址即可,如下所示: https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode=各省行政代码 例如广东省历史数据的地址为:https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode=440000 湖南省历史数据的地址为:https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode=430000

但是数据中并没有显示省的名称,因此需要写入每个省的名称。

为了便于写入各省的名称,我们需要生成一个各省行政代码和省名称对应的字典。

以一个简单的数据为例,展示生成字典的方法。

代码语言:javascript
复制
a = ['1','2','3','4']
b = ['q','w','e','r']

for i,j in zip(a, b):
    print(i,j)

1 q 2 w 3 e 4 r

代码语言:javascript
复制
{ i:j  for i,j in zip(a, b)}

{'1': 'q', '2': 'w', '3': 'e', '4': 'r'}

代码语言:javascript
复制
province_dict = {num:name for num,name in zip(today_province['id'],today_province['name'])}
代码语言:javascript
复制
# 查看前五个内容
count = 0
for i in province_dict:
    print(i,province_dict[i])
    count += 1
    if count == 5:
        break

420000 湖北 440000 广东 410000 河南 330000 浙江 430000 湖南

每一个省的列名是相同的,因此多个省的数据合并起来就可以存入一个数据中,数据合并演示的例子如下:

代码语言:javascript
复制
df1 = pd.DataFrame([{'a':1,'b':2,'c':3,},{'a':111,'b':222}])
df1
代码语言:javascript
复制
df2 = pd.DataFrame([{'a':9,'b':8,'c':7,},{'a':345,'c':789}])
df2
代码语言:javascript
复制
df1 = pd.concat([df1,df2],axis=0)
df1

按照上述方法,得到每一个省的数据后,进行合并。

代码语言:javascript
复制
start = time.time()
for province_id in province_dict: # 遍历各省编号
    
    try:
        # 按照省编号访问每个省的数据地址,并获取json数据
        url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode='+province_id
        r = requests.get(url, headers=headers)
        data_json = json.loads(r.text)
        
        # 提取各省数据,然后写入各省名称
        province_data = get_data(data_json['data']['list'],['date'])
        province_data['name'] = province_dict[province_id]
        
        # 合并数据
        if province_id == '420000':
            alltime_province = province_data
        else:
            alltime_province = pd.concat([alltime_province,province_data])
            
        print('-'*20,province_dict[province_id],'成功',
              province_data.shape,alltime_province.shape,
              ',累计耗时:',round(time.time()-start),'-'*20)
        
        # 设置延迟等待
        time.sleep(10)
        
    except:
        print('-'*20,province_dict[province_id],'wrong','-'*20)
代码语言:javascript
复制
save_data(alltime_province,'alltime_province')

alltime_province_2020_03_25.csv 保存成功!

4.3 世界各国历史数据爬取

接着找到下一个数据地址,对比后发现是意大利数据,首先尝试爬取意大利的历史数据,其他国家的数据可以用同样的方法爬取。

代码语言:javascript
复制
url_italy = 'https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode=15'  # 意大利的数据地址
r = requests.get(url_italy, headers=headers) # 进行访问
italy_json = json.loads(r.text) # 导出json数据
代码语言:javascript
复制
italy_json.keys()

dict_keys(['reqId', 'code', 'msg', 'data', 'timestamp'])

代码语言:javascript
复制
italy_json['data'].keys()

dict_keys(['list'])

结合页面观察,发现各国家的历史数据结构和各省的类似。

代码语言:javascript
复制
italy_json['data']['list'][0] # 查看数据内容

{'date': '2020-01-31', 'lastUpdateTime': None, 'today': {'confirm': 2, 'dead': 0, 'heal': 0, 'severe': None, 'storeConfirm': None, 'suspect': 0}, 'total': {'confirm': 2, 'dead': 0, 'heal': 0, 'severe': 0, 'suspect': 0}}

每个国家的数据格式和之前各省的几乎一样,因此使用定义好的方法生成数据,然后把国家名称写入数据。

代码语言:javascript
复制
data_italy = get_data(italy_json['data']['list'],['date']) # 生成数据
data_italy['name'] = '意大利' # 写入国家名称
data_italy.head()

因为原始数据中没有国家名称,为了得到每个国家的名称,需要生成国家编号和国家名称的键值对,这样就可以存储国家名称,在之前的世界各国实时数据today_world中有国家的编号和名称,可以用它来生成键值对。

代码语言:javascript
复制
today_world[['id','name']].head()
代码语言:javascript
复制
country_dict = {key:value for key,value in zip(today_world['id'], today_world['name'])}
代码语言:javascript
复制
# 查看前五个内容
count = 0
for i in country_dict:
    print(i,country_dict[i])
    count += 1
    if count == 5:
        break

9577772 突尼斯 9507896 塞尔维亚 0 中国 1 日本 2 泰国

通过每个国家的编号访问每个国家历史数据的地址,然后获取每一个国家的历史数据。

代码语言:javascript
复制
start = time.time()
for country_id in country_dict: # 遍历每个国家的编号
    
    try:
        # 按照编号访问每个国家的数据地址,并获取json数据
        url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-by-area-code?areaCode='+country_id
        r = requests.get(url, headers=headers)
        json_data = json.loads(r.text)
        
        # 生成每个国家的数据
        country_data = get_data(json_data['data']['list'],['date'])
        country_data['name'] = country_dict[country_id]

        # 数据叠加
        if country_id == '9577772':
            alltime_world = country_data
        else:
            alltime_world = pd.concat([alltime_world,country_data])
            
        print('-'*20,country_dict[country_id],'成功',country_data.shape,alltime_world.shape,
              ',累计耗时:',round(time.time()-start),'-'*20)
        
        time.sleep(10)

    except:
        print('-'*20,country_dict[country_id],'wrong','-'*20)
代码语言:javascript
复制
save_data(alltime_world,'alltime_world')

5.总结

本篇案例的主要内容是新冠肺炎疫情的数据采集,首先我们寻找合适的数据源,最终选择网易的实时疫情播报平台作为数据源,然后逐步地解析找到我们需要的数据,最后通过python爬虫获得我们得到了疫情的数据,包括中国各省和世界各国的实时数据,还包括中国整体、中国各省、世界各国的历史数据。通过这样一篇数据采集的案例让同学们学习到爬虫的基本方法,掌握这些方法能够提升数据采集的能力,并且可以使用在以后的学习和工作中。

注意:因为爬虫的对象是网页上的数据,如果这些网页的数据内容有所变动可能会导致采集无效。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 选择数据源
  • 2. 初步探索
  • 3. 实时数据爬取
    • 3.1 全国各省实时数据爬取
    • 3.2 世界各国实时数据爬取
    • 4.历史数据爬取
      • 4.1 全国历史数据爬取
        • 4.2 全国各省历史数据爬取
          • 4.3 世界各国历史数据爬取
          • 5.总结
          相关产品与服务
          数据库一体机 TData
          数据库一体机 TData 是融合了高性能计算、热插拔闪存、Infiniband 网络、RDMA 远程直接存取数据的数据库解决方案,为用户提供高可用、易扩展、高性能的数据库服务,适用于 OLAP、 OLTP 以及混合负载等各种应用场景下的极限性能需求,支持 Oracle、SQL Server、MySQL 和 PostgreSQL 等各种主流数据库。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档