爬虫数据清洗可视化案例之全球灾害数据
目标网站:全球灾害数据平台
温馨提示: 仅供学习交流使用
| requests() | lxml |
| pandas | numpy |
| matplotlib | seaborn |
| pprint |

明确需要爬取的内容:
-
国家地区
-
所属洲
-
总频次
-
受影响人口(万人)
-
死亡人口(人)
-
经济损失(千美元)
爬取步骤:
-
发送请求 模拟浏览器向服务器 发送请求
-
解析数据 提取想要的数据
-
保存数据 持久化保存 csv excel 数据库
一.发送请求
确定网页的构造 静态数据 or 动态数据
右击查看网页源代码 Ctrl+F 再搜索框中输入要获取的信息


返回的结果是没有 说明是动态的数据 需要抓包分析
此时 到网页主界面 快捷键(F12) or 右击 检查
点击网络 接着刷新当前界面 或 Ctrl+R(快捷键刷新)

同样地 Ctrl+F 打开搜索框 输入想获取的数据 在返回中的数据包中筛选 找到符合的那个

然后 点击标头 这里有请求的一些基本信息 可以查看

我们使用爬虫工具 快速构建 爬虫请求代码

右击相应的数据包 点击复制cURL(bash) 如上图所示 注意是bash
之后打开 工具 爬虫工具库-spidertools.cn

复制右边的代码 到Pycharm中
运行代码 检查想爬取的数据是否在返回的数据中

二. 解析数据
因为返回的是Json格式的数据 需要获取 返回的Json
这里我们可以使用 python 模块 pprint 可以让Json 格式的数据更加美观
import pprintpprint.pprint(response.json())
这样输出就会如下图所示

对比页面 找到想要爬取数据的位置 字段名
接着我们从 字典套列表的格式中提取数据 通过键值对取值 For循环遍历列表 取值
json_data = response.json()['data']['content']# 拿到想要的数据
for li in json_data:country = li['countryCn']state = li['continentCn']total = li['sum']death_people = li['death']influence_people = li['population']found_lose = li['loss']# 可以输出打印 对比数据是否符合
三. 保存数据
保存数据为 Excel文件
# 先将数据存储到 字典中
# 再定义一个列表 将数据添加进去
dit = {'国家': country,'州': state,'总频次': total,'死亡人数': death_people,'影响人数': influence_people,'损失': found_lose}# 保存数据 为excel
pd.DataFrame(lis).to_excel('global_disaster.xlsx',index=False)
多页爬取 查看请求体里面的data参数 直接 修改 循环即可 我这里写它全部的数据

温馨提示: 它这个 "Authorization" 参数具有时效性 逆向可解决 但不影响我们获取数据
这样就爬取到了我们想要的数据

以下是爬虫部分的全部代码
import pprintimport pandas as pd
import requests
import jsonheaders = {"Accept": "application/json, text/plain, */*","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Authorization": "Bearer b81b81e2-da54-4548-ac4f-4504ba437b51","Cache-Control": "no-cache","Connection": "keep-alive","Content-Type": "application/json;charset=UTF-8","Origin": "https://www.gddat.cn","Pragma": "no-cache","Referer": "https://www.gddat.cn/newGlobalWeb/","Sec-Fetch-Dest": "empty","Sec-Fetch-Mode": "cors","Sec-Fetch-Site": "same-origin","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0","sec-ch-ua": "\"Microsoft Edge\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "\"Windows\""
}
cookies = {"你的cookie"
}
url = "https://www.gddat.cn/gw/application-base-ris-portal/featureAnalysis/idisasterEmdatStandard/getIHistoryByCountry"
data = {"continentCn": "","frequency": "10","continent": [],"countryCode": [],"eventTypeCode": [],"maxaffected": "","maxdeaths": "","maxloss": "","minaffected": "","mindeaths": "","minloss": "","orderby": "sum","fromYear": "","toYear": "","Year": "","page": 0,"size": 204,
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, cookies=cookies, data=data)lis = []
json_data = response.json()['data']['content']
for li in json_data:country = li['countryCn']state = li['continentCn']total = li['sum']death_people = li['death']influence_people = li['population']found_lose = li['loss']dit = {'国家': country,'州': state,'总频次': total,'死亡人数': death_people,'影响人数': influence_people,'损失': found_lose}lis.append(dit)pd.DataFrame(lis).to_excel('global_disaster.xlsx',index=False)
数据清洗:

这里我们选择直接将缺失值删除
import pandas as pd# 读取文件
df = pd.read_excel('global_disaster.xlsx')# 将损失为缺失值的国家名字 展示出来
country_lose = df[df['损失'].isna()]['国家'].tolist()
# 删除缺失值
df.dropna(subset='损失', inplace=True)# 统计缺失值的数量
total = df['损失'].isna().sum()
# 展示处理后的前五条记录
print(df.head())


state_num = df['州'].unique()
没有不符合要求的命名

# iqr 处理异常值
lo = df['死亡人数'].quantile(0.25)
up = df['死亡人数'].quantile(0.75)
iqr = up - lo
lower = lo - 1.5 * iqr
upper = up + 1.5 * iqr# 未清理前的数据长度
before_clean = len(df)# df = df[(df['死亡人数'] > lower) & (df['死亡人数'] < upper)]
# 异常值 展示
# 异常值
false_death = df[(df['死亡人数'] < lower) | (df['死亡人数'] > upper)]
df['死亡人数'] = np.where((df['死亡人数'] < lower) | (df['死亡人数'] > upper),None,df['死亡人数']
)df.dropna(subset='死亡人数',inplace=True)print(len(df))# 思路是 将异常值用None值代替 后续删除

df['影响人数'] = pd.to_numeric(df['影响人数'], errors='coerce')
df['损失'] = pd.to_numeric(df['损失'], errors='coerce')
# print(df.info())
# print(df.head(5))

duplicated = df.duplicated().sum()
df.drop_duplicates(inplace=True)# print(len(df))

df['损失'] = (df['损失'] / 1000000).round(2)
print(df.head())

consistent_check = df[(df['总频次'] == 0) & (df['死亡人数'] > 0)]print(consistent_check)
数据可视化:
画出各州灾害总频次热力图 经济损失热力图 死亡人数热力图 平均灾害频次热力图
# 导包
import matplotlib.pyplot as plt
from matplotlib import rcParams
import seaborn as sns
import pandas as pddf = pd.read_excel('cleaned_global_disaster.xlsx')# 设置字体 为默认的黑体
rcParams['font.family'] = 'SimHei'region_summery = df.groupby(['州']).agg({'总频次': 'sum','死亡人数': 'sum','损失': 'sum','国家': 'count',
}).rename(columns={'国家': '国家数量', '损失': '损失(万元)'}).sort_values(by=['总频次', '损失(万元)'],ascending=[False, False])# 画子图
plt.subplot(2, 2, 1)
# annot = True 表示在热力图中显示对应的数值
# fmt = '.0f' 表示将数值类型的格式化为整数
# colormap的缩写 cmap 指定用红色系的颜色映射 Reds
# 设置cbar_Kws 用于设置颜色条的属性
# shrink 用于设置颜色条的大小
# annot_kws 用于设置注解的属性 字体 大小
# linewidths 设置单元格边框
sns.heatmap(region_summery[['总频次']], annot=True, fmt='.0f', cmap='Reds', linewidths=0.5,annot_kws={'size': 12, 'weight': 'bold'}, cbar_kws={'label': '灾害频次', 'shrink': 0.8})
plt.title('各州灾害总频次热力图')plt.subplot(2, 2, 2)
sns.heatmap(region_summery[['损失(万元)']],annot=True,annot_kws={'size': 12, 'weight': 'bold'},linewidths=0.5,fmt='.0f',cmap='Oranges',cbar_kws={'label': '损失(百万美元)', 'shrink': 0.8})plt.title('各州经济损失热力图')plt.subplot(2, 2, 3)
sns.heatmap(region_summery[['死亡人数']],annot=True,annot_kws={'size': 12, 'weight': 'bold'},linewidths=0.5,fmt='.0f',cmap='Purples',cbar_kws={'label': '死亡人数', 'shrink': 0.8})
plt.title('各州死亡人数热力图')plt.subplot(2, 2, 4)region_summery['平均频次'] = region_summery['总频次'] / region_summery['国家数量']
sns.heatmap(region_summery[['平均频次']],annot=True,annot_kws={'size': 12, 'weight': 'bold'},fmt='.1f',cmap='Blues',linewidths=0.5,cbar_kws={'label': '平均灾害频次', 'shrink': 0.8})
plt.title('各州平均灾害频次热力图')plt.tight_layout()
plt.show()
# print(region_summery)
如下图:

