前言
- 数据源:腾讯新闻肺炎疫情
- 数据爬取工具:requests-html (python 3.5及以上版本)
- 360极速浏览器 (其他浏览器可以类似找到‘开发者工具’)
需要注意的是,数据源之所以选择腾讯新闻,是因为腾讯新闻最容易抓取。你可以直接通过分析URL得到数据网址,并将这些数据存储为json文件。但是同样的过程,你并不能在其他门户新闻网站上进行。因此,腾讯新闻是最容易抓取疫情数据的网站。(或许有爬虫大佬能告诉我怎么在其他网站,比如百度新闻网站上爬取疫情数据,抱拳,感激不尽~)
步骤一:分析URL
首先,打开开发者工具,可以看到这样的画面:
其次,找到指向数据的URL。有两种方式:
1、如下图,我们分别标记
a
a
a,
b
b
b,
c
c
c和
d
d
d。
a
a
a: 点击‘Network’(不同浏览器可能名字不同);
b
b
b:在Search搜索框内输入在页面上看到的数据,比如,全国确诊数’34664’;
c
c
c:搜索框下方会出现URL;双击,点击
d
d
d。
经过上面的操作,可以看到下面图片的内容:
抄录其中的’Request URL’,为
URL=https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5&callback=jQuery34104299452223702189_1581164803507&_=1581164803508
参考大佬们(大佬A,大佬B)的博客,知道
- name=disease_h5 是数据位置
- callback=jQuery34104299452223702189_1581164803507&_=1581164803508返回当前时间戳的一个函数
我们只需要知道数据位置就行,因此,我们得到
URL=https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5
步骤二:数据采集
数据采集部分将主要参考@Hakuna_Matata_001的内容。
第一步:导入第三方库
import time
import json
import pandas as pd
import numpy as np
from datetime import datetime
# 这里requests-html需要python3.5以上版本
from requests_html import HTMLSession
第二步:抓取数据
# 创建会话
session = HTMLSession();
url = r'https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5';
# 从目标网址中读取数据
r = session.get(url).json();
# 将数据存储为json格式
data = json.loads(r['data']);
看看data里面都是什么
print(data.keys());
输出结果为
dict_keys(['chinaTotal', 'chinaAdd', 'lastUpdateTime', 'areaTree', 'chinaDayList', 'chinaDayAddList', 'isShowAdd', 'articleList'])
从输出结果可以看出来,data是一个字典,其中键有[‘国内总量’, ‘国内新增’, ‘更新时间’, ‘地区数据’, ‘每日数据’, ‘每日增加数据’, ‘是否显示增加’, ‘文章列表’]。
第三步:数据处理
我们最终想要什么样的数据?
- 国内疫情关于时间的变化序列
- 当天中国地区和世界其他地区的疫情情况
第一类数据储存在‘每日数据’和‘每日增加数据’中,第二类数据储存在‘地区数据’中。
在正式处理数据之前,我们不妨来看看 ‘国内总量’, ‘国内新增’, ‘更新时间’, ‘是否显示增加’和‘文章列表’这些数据分别是什么样子:
# 国内总量
print(data['chinaTotal']);
# 国内新增
print(data['chinaAdd']);
# 更新时间
print(data['lastUpdateTime']);
# 文章列表
print(data['articleList']);
打印出来,是这样的
# 国内总量
{'confirm': 37251, 'suspect': 28942, 'dead': 812, 'heal': 2651};
# 国内新增
{'confirm': 2653, 'suspect': 1285, 'dead': 89, 'heal': 599};
# 更新时间
2020-02-09 10:25:01;
# 文章列表太长,各位大佬可以自行打印~
下面,我们将国内疫情关于时间的变化序列存储成dataframe。
# 每日数据(将'date'列排在第一列)
chinaDayData = pd.DataFrame(data['chinaDayList'])[['date', 'confirm', 'suspect', 'dead', 'heal', 'deadRate', 'healRate']];
print(chinaDayData);
# 每日新增数据(将'date'列排在第一列)
chinaDayAddData = pd.DataFrame(data['chinaDayAddList'])[['date', 'confirm', 'suspect', 'dead', 'heal', 'deadRate', 'healRate']];
print(chinaDayAddData);
输出结果为
# 每日数据
date confirm suspect dead heal deadRate healRate
0 01.13 41 0 1 0 2.4 0.0
1 01.14 41 0 1 0 2.4 0.0
2 01.15 41 0 2 5 4.9 12.2
3 01.16 45 0 2 8 4.4 17.8
4 01.17 62 0 2 12 3.2 19.4
5 01.18 198 0 3 17 1.5 8.6
6 01.19 275 0 4 18 1.5 6.5
7 01.20 291 54 6 25 2.1 8.6
8 01.21 440 37 9 25 2.0 5.7
9 01.22 571 393 17 25 3.0 4.4
10 01.23 830 1072 25 34 3.0 4.1
11 01.24 1287 1965 41 38 3.2 3.0
12 01.25 1975 2684 56 49 2.8 2.5
13 01.26 2744 5794 80 51 2.9 1.9
14 01.27 4515 6973 106 60 2.3 1.3
15 01.28 5974 9239 132 103 2.2 1.7
16 01.29 7711 12167 170 124 2.2 1.6
17 01.30 9692 15238 213 171 2.2 1.8
18 01.31 11791 17988 259 243 2.2 2.1
19 02.01 14380 19544 304 328 2.1 2.3
20 02.02 17236 21558 361 475 2.1 2.8
21 02.03 20471 23214 425 632 2.1 3.1
22 02.04 24363 23260 491 892 2.0 3.7
23 02.05 28060 24702 564 1153 2.0 4.1
24 02.06 31211 26359 637 1542 2.0 4.9
25 02.07 34598 27657 723 2052 2.1 5.9
26 02.08 37251 28942 812 2651 2.2 7.1
# 每日新增数据
date confirm suspect dead heal deadRate healRate
0 01.20 77 27 0 0 0.0 0.0
1 01.21 149 53 3 0 2.0 0.0
2 01.22 131 257 8 0 6.1 0.0
3 01.23 259 680 8 6 3.1 2.3
4 01.24 444 1118 16 3 3.6 0.7
5 01.25 688 1309 15 11 2.2 1.6
6 01.26 769 3806 24 2 3.1 0.3
7 01.27 1771 2077 26 9 1.5 0.5
8 01.28 1459 3248 26 43 1.8 2.9
9 01.29 1737 4148 38 21 2.2 1.2
10 01.30 1982 4812 43 47 2.2 2.4
11 01.31 2102 5019 46 72 2.2 3.4
12 02.01 2590 4562 45 85 1.7 3.3
13 02.02 2829 5173 57 147 2.0 5.2
14 02.03 3235 5072 64 157 2.0 4.9
15 02.04 3893 3971 65 262 1.7 6.7
16 02.05 3697 5328 73 261 2.0 7.1
17 02.06 3143 4833 73 387 2.3 12.3
18 02.07 3401 4214 86 510 2.5 15.0
19 02.08 2657 3916 89 600 3.3 22.6
通过观察,我们发现 每日新增数据 可以通过 每日数据 得到,而且 每日新增数据 记录日期还较少,因此,我们仅用 每日数据 。
我们还需要看看每一列的数据类型,如下:
print(chinaDayData.info());
Data columns (total 7 columns):
date 20 non-null object
confirm 20 non-null int64
suspect 20 non-null int64
dead 20 non-null int64
heal 20 non-null int64
deadRate 20 non-null object
healRate 20 non-null object
dtypes: int64(4), object(3)
memory usage: 1.2+ KB
None
可以看到,不但’data’是object类型,'deadRate’和’healRate’同样也是object类型。为了后面处理方便,我们需要将’deadRate’和’healRate’的数据类型改成float,操作如下:
# 将'deadRate'列的数据类型改成float
chinaDayData.deadRate = chinaDayData.deadRate.map(float);
# 将'healRate'列的数据类型改成float
chinaDayData.healRate = chinaDayData.healRate.map(float);
接下来,我们根据 每日数据 的第一列生成 每日增加数据 列,默认第一天增加数为0。鉴于当天确认数都是凌晨以后,所以当天增加计算公式为 当天增加数=当天确认数-昨天确认数。
# 计算第二天直到最后一天的每天增加数
add = [chinaDayData['confirm'][i]-chinaDayData['confirm'][i-1] for i in range(1, len(chinaDayData['confirm']))];
# 默认第一天增加数为0
add.insert(0, 0);
# 创建新的列add
chinaDayData['add'] = add;
# 打印数据
print(chinaDayData);
date confirm suspect dead heal deadRate healRate add
0 01.13 41 0 1 0 2.4 0.0 0
1 01.14 41 0 1 0 2.4 0.0 0
2 01.15 41 0 2 5 4.9 12.2 0
3 01.16 45 0 2 8 4.4 17.8 4
4 01.17 62 0 2 12 3.2 19.4 17
5 01.18 198 0 3 17 1.5 8.6 136
6 01.19 275 0 4 18 1.5 6.5 77
7 01.20 291 54 6 25 2.1 8.6 16
8 01.21 440 37 9 25 2.0 5.7 149
9 01.22 571 393 17 25 3.0 4.4 131
10 01.23 830 1072 25 34 3.0 4.1 259
11 01.24 1287 1965 41 38 3.2 3.0 457
12 01.25 1975 2684 56 49 2.8 2.5 688
13 01.26 2744 5794 80 51 2.9 1.9 769
14 01.27 4515 6973 106 60 2.3 1.3 1771
15 01.28 5974 9239 132 103 2.2 1.7 1459
16 01.29 7711 12167 170 124 2.2 1.6 1737
17 01.30 9692 15238 213 171 2.2 1.8 1981
18 01.31 11791 17988 259 243 2.2 2.1 2099
19 02.01 14380 19544 304 328 2.1 2.3 2589
20 02.02 17236 21558 361 475 2.1 2.8 2856
21 02.03 20471 23214 425 632 2.1 3.1 3235
22 02.04 24363 23260 491 892 2.0 3.7 3892
23 02.05 28060 24702 564 1153 2.0 4.1 3697
24 02.06 31211 26359 637 1542 2.0 4.9 3151
25 02.07 34598 27657 723 2052 2.1 5.9 3387
26 02.08 37251 28942 812 2651 2.2 7.1 2653
我们接着处理 地区数据(areaData=data[‘areaTree’])。 地区数据是一个字典list,一个国家用一个字典储存。
# 地区数据
areaData=data['areaTree']
print('总共有%d个国家,包括' % len(areaData));
for country in areaData:
print(country['name']);
总共有25个国家,包括
中国
日本
新加坡
泰国
韩国
马来西亚
澳大利亚
越南
德国
美国
法国
阿联酋
加拿大
英国
印度
意大利
菲律宾
俄罗斯
芬兰
斯里兰卡
西班牙
瑞典
柬埔寨
尼泊尔
比利时
中国地区作为一个整体,里面包含整个中国的整体数据,以及每个省份及城市的具体数据。我们将直接提取各个省份及城市的信息。
# 地区数据,是一个列表
areaData=data['areaTree'];
# 中国数据,是一个字典,其中,各省份信息储存在'children'里面
chinaData = areaData[0];
# 取出各省份信息,是一个列表
provinces = chinaData['children'];
print('总共有%d个省份,包括' % len(provinces));
for province in provinces:
print(province['name']);
总共有34个省份,包括
湖北
广东
浙江
河南
湖南
安徽
江西
江苏
重庆
山东
四川
北京
黑龙江
上海
福建
陕西
河北
广西
云南
海南
山西
辽宁
贵州
天津
甘肃
吉林
内蒙古
宁夏
新疆
香港
青海
台湾
澳门
西藏
以湖北省为例,每一个省份又是一个字典,包含该省每个城市的信息。
Hubei = provinces[0];
city = [];
province = [];
total_confirm = [];
total_dead = [];
total_heal = [];
total_deadRate = [];
total_healRate = [];
for c in Hubei['children']:
# 城市名称
city.append(c['name']);
# 确诊总数
total_confirm.append(c['total']['confirm']);
# 治愈总数
total_heal.append(c['total']['heal']);
# 死亡总数
total_dead.append(c['total']['dead']);
# 总体死亡率
total_deadRate.append(c['total']['deadRate']);
# 总体治愈率
total_healRate.append(c['total']['healRate']);
Hubei_info = pd.DataFrame({'city': city, 'confirm': total_confirm, 'heal': total_heal, 'dead': total_dead, 'healRate(%)': total_healRate, 'deadRate(%)': total_deadRate});
print(Hubei_info);
city confirm heal dead healRate(%) deadRate(%)
0 武汉 14982 877 608 5.85 4.06
1 孝感 2436 45 29 1.85 1.19
2 黄冈 2141 135 43 6.31 2.01
3 荆州 997 40 13 4.01 1.30
4 襄阳 988 40 7 4.05 0.71
5 随州 984 23 9 2.34 0.91
6 黄石 760 54 2 7.11 0.26
7 宜昌 711 36 8 5.06 1.13
8 荆门 663 48 19 7.24 2.87
9 鄂州 639 42 21 6.57 3.29
10 咸宁 493 23 4 4.67 0.81
11 十堰 467 40 0 8.57 0.00
12 仙桃 379 16 5 4.22 1.32
13 天门 197 1 10 0.51 5.08
14 恩施州 171 20 0 11.70 0.00
15 潜江 82 2 2 2.44 2.44
16 神农架 10 2 0 20.00 0.00
17 地区待确认 0 3 0 NaN NaN
通过类似的操作,现在直接将整个中国的数据转化成dataframe进行输出,代码如下:
city = [];
province = [];
total_confirm = [];
total_dead = [];
total_heal = [];
total_deadRate = [];
total_healRate = [];
for p in provinces:
for c in p['children']:
# 省份名称
province.append(p['name']);
# 城市名称
city.append(c['name']);
# 确诊总数
total_confirm.append(c['total']['confirm']);
# 治愈总数
total_heal.append(c['total']['heal']);
# 死亡总数
total_dead.append(c['total']['dead']);
# 总体死亡率
total_deadRate.append(c['total']['deadRate']);
# 总体治愈率
total_healRate.append(c['total']['healRate']);
china_info = pd.DataFrame({'city': city, 'province': province, 'confirm': total_confirm, 'heal': total_heal, 'dead': total_dead, 'healRate(%)': total_healRate, 'deadRate(%)': total_deadRate});
print(china_info);
city province confirm heal dead healRate(%) deadRate(%)
0 武汉 湖北 14982 877 608 5.85 4.06
1 孝感 湖北 2436 45 29 1.85 1.19
2 黄冈 湖北 2141 135 43 6.31 2.01
3 荆州 湖北 997 40 13 4.01 1.30
4 襄阳 湖北 988 40 7 4.05 0.71
.. ... ... ... ... ... ... ...
421 西宁 青海 15 3 0 20.00 0.00
422 海北州 青海 3 0 0 0.00 0.00
423 地区待确认 台湾 18 1 0 5.56 0.00
424 地区待确认 澳门 10 1 0 10.00 0.00
425 地区待确认 西藏 1 0 0 0.00 0.00
[426 rows x 7 columns]
检查每列的数据类型
print(china_info.info());
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 426 entries, 0 to 425
Data columns (total 7 columns):
city 426 non-null object
province 426 non-null object
confirm 426 non-null int64
heal 426 non-null int64
dead 426 non-null int64
healRate(%) 417 non-null float64
deadRate(%) 417 non-null float64
dtypes: float64(2), int64(3), object(2)
memory usage: 23.4+ KB
None
最后,将其他国家的数据作为一个整体,用dataframe输出
foreign_country = [];
foreign_confirm = [];
foreign_dead = [];
foreign_heal = [];
foreign_deadRate = [];
foreign_healRate = [];
for i in range(1, len(areaData)):
# 国名
foreign_country.append(areaData[i]['name']);
# 确认总数
foreign_confirm.append(areaData[i]['total']['confirm']);
# 死亡总数
foreign_dead.append(areaData[i]['total']['dead']);
# 治愈总数
foreign_heal.append(areaData[i]['total']['heal']);
# 总体死亡率
foreign_deadRate.append(areaData[i]['total']['deadRate']);
# 总体治愈率
foreign_healRate.append(areaData[i]['total']['healRate']);
foreigns = pd.DataFrame({'country': foreign_country, 'confirm': foreign_confirm, 'dead': foreign_dead, 'heal': foreign_heal, 'deadRate': foreign_deadRate, 'healRate': foreign_healRate});
print(foreigns);
country confirm dead heal deadRate healRate
0 中国 37263 813 2767 2.18 7.43
1 日本 89 0 1 0.00 1.12
2 新加坡 40 0 2 0.00 5.00
3 泰国 32 0 8 0.00 25.00
4 韩国 25 0 3 0.00 12.00
5 马来西亚 17 0 2 0.00 11.76
6 澳大利亚 15 0 5 0.00 33.33
7 越南 14 0 3 0.00 21.43
8 德国 13 0 0 0.00 0.00
9 美国 12 0 1 0.00 8.33
10 法国 11 0 0 0.00 0.00
11 加拿大 7 0 0 0.00 0.00
12 阿联酋 7 0 0 0.00 0.00
13 英国 3 0 0 0.00 0.00
14 菲律宾 3 1 0 33.33 0.00
15 意大利 3 0 0 0.00 0.00
16 印度 3 0 0 0.00 0.00
17 俄罗斯 2 0 0 0.00 0.00
18 芬兰 1 0 1 0.00 100.00
19 斯里兰卡 1 0 1 0.00 100.00
20 西班牙 1 0 0 0.00 0.00
21 瑞典 1 0 0 0.00 0.00
22 柬埔寨 1 0 0 0.00 0.00
23 尼泊尔 1 0 0 0.00 0.00
24 比利时 1 0 0 0.00 0.00
检查每一列的数据类型
print(foreigns.info());
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 25 entries, 0 to 24
Data columns (total 6 columns):
country 25 non-null object
confirm 25 non-null int64
dead 25 non-null int64
heal 25 non-null int64
deadRate 25 non-null float64
healRate 25 non-null float64
dtypes: float64(2), int64(3), object(1)
memory usage: 1.3+ KB
None
至此,我们将所需数据存入三个DataFrame中
- 中国疫情历史数据:chinaDayData
- 中国各省份城市的当天疫情数据:china_info
- 其他发生疫情的国家整体疫情数据:foreigns
步骤三:可视化
抱歉,这里请转到大佬@Hakuna_Matata_001的博文,大佬的博文里用到pyecharts,可以作出地图,非常棒,但是本人目前运行pyecharts遇到问题,暂时无法解决,等后面定补上pyecharts的内容!抱拳!~