Jul 8, 2022 使用python爬取并分析疫情数据 目录数据获取 .................................................................... 1数据分析和展示 ............................................................. 1数据读入和清洗 .......................................................... 1累计确诊数排名前 20 的国家名称及其数量 ............................... 115 天中,每日新增确诊数累计排名前 10 个国家的每日新增确诊数据的曲线图 ..................................................................... 1累计确诊人数占国家总人口比例最高的 10 个国家 ........................ 1死亡率(累计死亡人数/累计确诊人数)最低的 10 个国家 ................ 1用饼图展示各个国家的累计确诊人数的比例 .............................. 1展示全球各个国家累计确诊人数的箱型图,有平均值 .................... 1另外一些数据 ............................................................ 1全世界应对新冠疫情最好的10个国家 ...................................... 1预测分析 .................................................................... 1预测方法 ................................................................. 1预测程序 ................................................................. 1结果以及分析 ............................................................ 1代码网址 .................................................................... 1数据获取选取wikipedia上的疫情数据,总数据页如下图 1 总数据页每个国家的数据都可以从表格中解析出来,例如United States的详细数据的地址为 Unitedd States。 具体数据可以从下图中的表格解析:图 2 United States数据页仔细分析之后我们可以写以下爬虫代码:1import scrapy2from ..items import FordataItem34class mySpider(scrapy.spiders.Spider):5 name = "forData"67 def start_requests(self):8 beginUrl= "https://en.wikipedia.org/w/index.php?title=Template:COVID-19_pandemic_data"9 myHeader = {10 'Content-Type': 'application/json',11 'User-Agent':'Mozilla/5.0 (X11; Fedora; Linux x86_64;rv:80.0) Gecko/20100101 Firefox/80.0'12 }13 #先爬出数据首页,找到有哪些国家14 yieldscrapy.Request(url=beginUrl,callback=self.parseCountryUrl,headers=myHeader)1 def parseCountryUrl(self,response):2 cnt=03 for each in response.xpath('/html/body/div[3]/div[3]/div[5]/div[1]/div[4]/div[2]/table/tbody/tr'):4 countryName=each.xpath('th[2]/a/text()').get()5 countryUrl=each.xpath('th[2]/a/@href').extract()6 if(countryName!=None and len(countryUrl)>0):7 cnt=cnt+18 print(countryName,countryUrl)9 countryDataUrl= "https://en.wikipedia.org"+countryUrl[0]10 myHeader = {11 'Content-Type': 'application/json',12 'User-Agent':'Mozilla/5.0 (X11; Fedora; Linuxx86_64; rv:81.0) Gecko/20100101 Firefox/81.0'13 }14 yieldscrapy.Request(url=countryDataUrl,headers=myHeader,callback=lambdaresponse,name=countryName:self.parseCountryCase(response,name))15 #再分别爬取每个国家的数据16 print(cnt)1718 def parseCountryCase(self,response,name):19 ct=020 for tr in response.css('tr.mw-collapsible'):21 item=FordataItem()22 item['country']=name23 item['date']=tr.xpath('td[1]/text()').get()24 item['cases']=tr.xpath('td[3]/span[1]/text()').get()25 item['deaths']=tr.xpath('td[4]/span[1]/text()').get()26 #解析出每个国家的名字、日期、病例、死亡数27 if('2020' in item['date']):28 ct=ct+129 yield(item)30 if(ct==0):31 print("没有单日数据的的国家:",name)数据前几行如下:图 3 疫情数据另外,这个网页上并没有人口总数的数据可以从https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population上继续爬取。图 4 国家人口对应表的数据来源可以写出如下爬虫1import scrapy2from ..items import ForpopulationItem34class mySpider(scrapy.spiders.Spider):5 name = "forPopulation"67 def start_requests(self):8 beginUrl= "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"9 myHeader = {10 'Content-Type': 'application/json',11 'User-Agent':'Mozilla/5.0 (X11; Fedora; Linux x86_64;rv:80.0) Gecko/20100101 Firefox/80.0'12 }13 #网页请求14 yieldscrapy.Request(url=beginUrl,callback=self.parsePopulation,headers=myHeader)1516 def parsePopulation(self,response):17 for tr in response.xpath('/html/body/div[3]/div[3]/div[5]/div[1]/table/tbody/tr'):18 #数据解析19 item=ForpopulationItem()20 item['country']=tr.xpath('td[1]/a/text()').get()21 item['population']=tr.xpath('td[2]/text()').get()22 if(item['country'] and item['population']):23 item['population'].replace(',','')24 print(item)25 yield item爬取结果如下图 5 国家人口对应表数据分析和展示数据读入和清洗从前面爬取的数据文件1myData.csv和1population.csv中读取数据。 对于疫情数据,将空值填充为0,只保留日起在12020-11-30到12020-12-15之间的数据。 将病例数1cases和1deaths格式转换为1int64。 根据1country和1date 进行去重。1import pandas2import numpy3import matplotlib4import matplotlib.pyplot as plt5from brokenaxes import brokenaxes6import seaborn78org=pandas.read_csv('myData.csv')9org=org.fillna('0')10org['cases']=org['cases'].str.replace('\D+','').astype(int)11org['deaths']=org['deaths'].str.replace('\D+','').astype(int)12org=org[org['date']<'2020-12-16']13org=org[org['date']>'2020-11-29']14org.drop_duplicates(subset =['country','date'],keep='first',inplace=True)15myData=org16population=pandas.read_csv('population.csv')17population['population']=population['population'].str.replace('\D+','').astype(int)1819print("收集到的",len(myData['country'].value_counts(dropna=False)),"个国家的数据")收集到的 135 个国家的数据1## 15天中,全球新冠疫情的总体变化趋势234####################################5#a) 15 天中,全球新冠疫情的总体变化趋势#6####################################78print("\n\n====15 天中,全球新冠疫情的总体变化趋势======")910#这十六天内的全球累计病例11totalCase=[0]*161213#这16天内的全球死亡累计病例14totalDeaths=[0]*161516#December表示日期,用字符串存储,方便图例生成17December=['']*161819#根据国家进行分组,全球数量=个国家数量之和,计算累计病例和死亡病例20casesSumByDay=myData.pivot_table(index='date',values=['cases'],aggfunc=sum)21deathsSumByDay=myData.pivot_table(index='date',values=['deaths'],aggfunc=sum)2223December[0]='2020-11-30'24totalCase[0]=casesSumByDay.loc['2020-11-30']['cases']25totalDeaths[0]=deathsSumByDay.loc['2020-11-30']['deaths']2627for i in range(1,16):28December[i]='2020-12-'+('0' if i<10 else '')+str(i)29totalCase[i]=casesSumByDay.loc[December[i]]['cases']30totalDeaths[i]=deathsSumByDay.loc[December[i]]['deaths']3132print("11-30到12-15感染病例:",totalCase)33print("11-30到12-15死亡病例:",totalDeaths)3435# matplotlib绘图显示中文3637plt.rcParams["font.family"]="SimHei"3839#画图部分40mycolors = ['tab:red', 'tab:blue']41fig, ax = plt.subplots(1,1,figsize=(16, 9), dpi= 300)42x = December43y = numpy.vstack([totalDeaths,totalCase])44labs=['累积死亡数','累积感染病例']45ax = plt.gca()46ax.stackplot(x, y, labels=labs, colors=mycolors, alpha=0.8)47plt.title("15 天中,全球新冠疫情的总体变化趋势")48ax.legend(fontsize=10, ncol=4)49plt.xticks(rotation=30,fontsize=10) # 这里是调节横坐标的倾斜度50plt.gca().spines["top"].set_alpha(0)51plt.gca().spines["bottom"].set_alpha(.3)52plt.gca().spines["right"].set_alpha(0)53plt.gca().spines["left"].set_alpha(.3)54plt.savefig('a.png')1====15 天中,全球新冠疫情的总体变化趋势======211-30到12-15感染病例: [58806605, 59867806, 59924956, 60557860,61163129, 59527002, 60033395, 62585050, 62008006, 63602677, 65008985,65672468, 63968990, 64505434, 67261669, 68293227]311-30到12-15死亡病例: [1471087, 1492594, 1494629, 1506452, 1517510,1475608, 1482627, 1541590, 1516612, 1564328, 1576069, 1588076,1541701, 1549901, 1611817, 1633944]图 6 15 天中,全球新冠疫情的总体变化趋势累计确诊数排名前 20 的国家名称及其数量1####################################################2#b) 累计确诊数排名前 20 的国家名称及其数量; #3####################################################4print("\n\n=======累计确诊数排名前 20 的国家名称及其数量=========")56#总的累计数量只需要看最后一天的数量7casesMaxByCountry=myData.pivot_table(index='country',values=['cases'],aggfunc=max)8casesMaxByCountry=casesMaxByCountry.sort_values('cases',ascending=False)910print(casesMaxByCountry.head(20))输出结果:1=======累计确诊数排名前 20 的国家名称及其数量=========2cases3country4United States 160222975India 99061656Brazil 69700347Russia 27079458France 23914479Turkey 189844710United Kingdom 188811611Italy 187057612Spain 176221213Argentina 151018614Colombia 144464615Germany 135151016Mexico 126720217Poland 114744618Iran 112347419Peru 98767520Ukraine 91970421South Africa 87367922Indonesia 62942923Netherlands 62857715 天中,每日新增确诊数累计排名前 10 个国家的每日新增确诊数据的曲线图1##########################################################################2#c)15 天中,每日新增确诊数累计排名前 10 个国家的每日新增确诊数据的曲线图;#3##########################################################################4mycolors=['tab:blue','tab:orange','tab:green','tab:red','tab:purple','tab:brown','tab:pink','tab:gray','tab:olive','tab:cyan']5plt.clf()67#每个国家的12-1之前的累计数量8casesMinByCountry=myData.pivot_table(index='country',values=['cases'],aggfunc=min)910#每个国家12-1到12-15的新增数量11decCaseMaxByCountry=(casesMaxByCountry-casesMinByCountry).sort_values('cases', ascending=False)1213cnt=014countryCName=[]15figC=[0]*1016for index,row in decCaseMaxByCountry.iterrows():17cnt=cnt+118countryCName.append(index)19yi=myData[myData['country']==index]['cases'].tolist()20for j in range(15,0,-1):#得到每日变化数据21yi[j]=yi[j]-yi[j-1]22figC[cnt-1],=plt.plot(December[1:],yi[1:],color=mycolors[cnt-1],linewidth=2.0,linestyle='-.')23if(cnt>=10):#只保留前十个国家24break2526plt.title("每日新增确诊排名前 10 的国家的数据曲线图")27plt.legend(handles=figC,labels=countryCName)28plt.savefig('c.png')图 7 每日新增确诊排名前 10 的国家的数据曲线图累计确诊人数占国家总人口比例最高的 10 个国家1################################################2#d) 累计确诊人数占国家总人口比例最高的 10 个国家 #3################################################4print("\n\n=====累计确诊人数占国家总人口比例最高的 10 个国家======")56#这会用到populaton.csv的数据7#累计确诊比例8casesRatio=[]9for index,row in casesMaxByCountry.iterrows():10tmp=population[population['country']==index]['population']11if(len(tmp)>0):#这里需要注意没有人口数据的国家12casesRatio.append({'casesRatio':row['cases']/tmp.iloc[0],'name':index})1314#升序排序取最后10个15casesRatio.sort(key=lambda x:x['casesRatio'])16for i in casesRatio[-1:-10:-1]:17print(i)结果如下:1=====累计确诊人数占国家总人口比例最高的 10 个国家======2{'casesRatio': 0.09561146718594844, 'name': 'Andorra'}3{'casesRatio': 0.06748037079864816, 'name': 'Luxembourg'}4{'casesRatio': 0.05548972112860494, 'name': 'Czech Republic'}5{'casesRatio': 0.05271911310260917, 'name': 'Belgium'}6{'casesRatio': 0.05243676244828293, 'name': 'Georgia'}7{'casesRatio': 0.05198869490976536, 'name': 'Qatar'}8{'casesRatio': 0.04872524937150579, 'name': 'Moldova'}9{'casesRatio': 0.04842282774074547, 'name': 'United States'}10{'casesRatio': 0.046797668330376366, 'name': 'Slovenia'}死亡率(累计死亡人数/累计确诊人数)最低的 10 个国家1#########################################################2#e) 死亡率(累计死亡人数/累计确诊人数)最低的 10 个国家; #3#########################################################4print("\n\n====死亡率(累计死亡人数/累计确诊人数)最低的 10 个国家=======")56#每个国家死亡人数7deathsMaxByCountry=myData.pivot_table(index='country',values=['deaths'],aggfunc=max)8#每个国家的死亡率9deathsRatio=deathsMaxByCountry.rename(columns={'deaths':'deathsRatio'})10deathsRatio=deathsRatio/casesMaxByCountry.rename(columns={'cases':'deathsRatio'})11print((deathsRatio.sort_values('deathsRatio')).head(10))结果如下:1====死亡率(累计死亡人数/累计确诊人数)最低的 10 个国家=======2deathsRatio3country4Seychelles 0.0000005Guyana 0.0000006Saint Lucia 0.0000007Faroe Islands 0.0000008Saint Kitts and Nevis 0.0000009Falkland Islands 0.00000010Brunei 0.00000011Dominica 0.00000012Saint Vincent and The Grenadines 0.00000013Singapore 0.000497用饼图展示各个国家的累计确诊人数的比例12########################################3#f) 用饼图展示各个国家的累计确诊人数的比例#4########################################5print("\n\n=====各个国家的累计确诊人数的比例=======")67#把占比最大的几个国家列出来,直到这些国家占比超过75%,其他国家表示为other8cnt=09figData=[]10for index,row in casesMaxByCountry.iterrows():11cnt=cnt+row['cases']12if cnt/totalCase[15]>0.75:13break14figData.append((index,row['cases']))15figData.append(('others',(totalCase[15]-cnt)))1617#输出一下具体数据18print(figData)1920#画图部分21plt.clf()22fig, ax = plt.subplots(figsize=(10, 5), ncols=2,dpi=300)23ax1, ax2 = ax.ravel()24patches, texts = ax1.pie(dict(figData).values(),25shadow=True,startangle=90,26labels=[ str(country)+','+str('%.2lf'%(tot*100/totalCase[15]))+'%'for country, tot in figData])27ax1.set_title('各个国家的累计确诊人数的比例')28ax2.axis('off')29ax2.legend(patches, [i for i,j in figData], loc='center left')30plt.tight_layout()31plt.savefig('f.png')图 8 各个国家的累计确诊人数的比例1=====各个国家的累计确诊人数的比例=======2[('United States', 16022297), ('India', 9906165), ('Brazil',6970034), ('Russia', 2707945), ('France', 2391447), ('Turkey',1898447), ('United Kingdom', 1888116), ('Italy', 1870576), ('Spain',1762212), ('Argentina', 1510186), ('Colombia', 1444646), ('Germany',1351510), ('Mexico', 1267202), ('others', 16154998)]展示全球各个国家累计确诊人数的箱型图,有平均值1################################################2#g) 展示全球各个国家累计确诊人数的箱型图,要有平均值#3################################################4print("\n\n=====全球各个国家累计确诊人数的箱型图=====")56plt.clf()7fig, ax = plt.subplots(figsize=(5, 10),dpi=300)8#图中的黄点是平均数据值9seaborn.stripplot(y='cases',data={'cases':[totalCase[15]/135]},color="orange", size=2.5)10seaborn.boxplot(data=casesMaxByCountry,linewidth=0.3,fliersize=1)11plt.title("全球各个国家累计确诊人数的箱型图")12plt.savefig('g.png')图 9 全球各个国家累计确诊人数的箱型图另外一些数据115日新增确诊占累积确诊病例最小的20个国家23########################################45# 15日新增确诊占累积确诊病例最小的20个国家#67########################################8minRatio=(decCaseMaxByCountry/casesMaxByCountry).sort_values('cases')9print(minRatio.head(20))12cases3country4Comoros 0.0000005Singapore 0.0021086Australia 0.0050997Malawi 0.0085538Isle of Man 0.0107249Suriname 0.01282310Brunei 0.01315811Guernsey 0.01374612Sierra Leone 0.01591213Qatar 0.01726514Ivory Coast 0.01777415New Zealand 0.01908416Bolivia 0.02036317Nepal 0.02264518Benin 0.02427219Peru 0.02437020São Tomé and Príncipe 0.02475221Maldives 0.02670622Kuwait 0.02777623Iceland 0.029580全世界应对新冠疫情最好的10个国家注意:爬取的数据没有包含中国的数据。显而易见的,感染人数过多(大于一百万)的国家应对疫情肯定不是最好的。感染人数较少(小于100)也不需要考虑,这可能疫情并没有爆发,只是几个境外输入病例。 我们只考虑疫情严重的几个国家。这之后按照人口递减排序,取前10个。12#####################3#最好的10个国家 #4#####################5print("\n\n======应对疫情最好的10个国家========")6good=[]7for index,rows in casesMaxByCountry.iterrows():8tmp=population[population['country']==index]['population']9if((100<rows['cases']<1000000) and len(tmp)>0):10good.append({'population':tmp.iloc[0],'name':index})1112good.sort(key=lambda x:-x['population'])1314for i in range(10):15print(good[i]['name'])1======应对疫情最好的10个国家========2Indonesia3Pakistan4Nigeria5Japan6Philippines7Ethiopia8Egypt9Vietnam10South Africa11Myanmar预测分析预测方法采用线性回归的方法进行预测。 新冠疫情存在一定的趋势,但目前数据比较少(只有10天),可以假设不存在季节性。 这样,可以采用1线性回归的方法进行预测。预测程序12###################################34# 预测分析 #56###################################78from sklearn import linear_model9regr = linear_model.LinearRegression()1011# 拟合1213regr.fit(numpy.array([i for i in range(10)]).reshape(-1, 1),totalCase[1:11])1415# 得到直线的斜率、截距1617a, b = regr.coef_, regr.intercept_1819# 给出待预测日期2021predictDate = numpy.array([i for i in range(10,16)]).reshape(-1,1)2223#输出一下预测值24print(regr.predict(predictDate))2526#画图部分27plt.clf()28fig, ax = plt.subplots(figsize=(16, 10),dpi=300)29plt.scatter(December, totalCase, color='blue')30plt.plot(December[1:], regr.predict(numpy.array([i for i inrange(15)]).reshape(-1,1)), color='red', linewidth=4)31plt.savefig('i.png')1======预测分析========2[64229138.06666667 64738456.51515152 65247774.9636363765757093.41212121366266411.86060607 66775730.30909091]结果以及分析图 10 预测分析最后五天的数据拟合程度较好。对于短期的数据,线性回归应该是较好的一种预测方法。 然而,再实际问题中,还需要考虑各种影响因素。 比如,各个国家政策的调整,病毒的变异等等。代码网址code