国家数据网的人口比率

一.主题式网络爬虫设计方案

1.主题式网络爬虫名称:国家数据网不同年份的人口比率

2.主题式网络爬虫爬取的内容:人口出生率死亡率及自然增长率

3.设计方案概述:

实现思路:爬取网站内容,之后分析提取需要的数据,进行数据清洗,之后数据可视化,并计算不同比率的相关系数

技术难点:因为用的是json分析,所以需要通过查找的方式获取数据,以及后面的3D模型图需要将整数转成浮点数

二、主题页面的结构特征分析

1.主题页面的结构与特征分析:打开开发者工具,通过逐个查找找到需要数据的所在位置,发现所需要的数据都在<tbody>

下的<tr>中

2.

3.

三、网络爬虫程序设计

1.数据爬取与采集

import matplotlib.pyplot as plt
import numpy as np
import matplotlib
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
import pickle
matplotlib.rcParams[
'font.sans-serif']=['SimHei'] # 用黑体显示中文 matplotlib.rcParams['axes.unicode_minus']=False # 正常显示负号 dfwds = '[{"wdcode": "sj", "valuecode": "LAST21"}, {"wdcode":"zb","valuecode":"A0302"}]' #网址的dfwds为上面所写的,以便更快的获取需要的数据 url='http://data.stats.gov.cn/easyquery.htm?m=QueryData&dbcode=hgnd&rowcode=sj&colcode=zb&wds=[]&dfwds={}' #获取url response = requests.get(url.format(dfwds)) #获取网页的数据 z=response.json() #获取网址的json z=z['returndata'] #通过字典的key值,获取数据
z=z['datanodes'] print(z)


获取dfwds和url

2.对数据进行清洗和处理

birth_rate={}    #分别设置三个字典,用来装后面的值
death_rate={}
natural_growth_rate={}
for i in range(len(z)//3):     #通过循环,获取结果
    #print("年份{}:出生率{}".format(z[i]['wds'][1]['valuecode'],z[i]['data']['data']))
    birth_rate[z[i]['wds'][1]['valuecode']]=z[i]['data']['data']
#print(birth_rate)
for i in range(len(z)//3,len(z)//3*2):
    #print("年份{}:死亡率{}".format(z[i]['wds'][1]['valuecode'],z[i]['data']['data']))
    death_rate[z[i]['wds'][1]['valuecode']]=z[i]['data']['data']
#print(death_rate)
for i in range(len(z)//3*2,len(z)):
    #print("年份{}:自然增长率{}".format(z[i]['wds'][1]['valuecode'],z[i]['data']['data']))
    natural_growth_rate[z[i]['wds'][1]['valuecode']]=z[i]['data']['data']
#print(natural_growth_rate)
years=list(birth_rate.keys())   #把字典里面的key和value都拿出来,分别转成列表类型
rate1=list(birth_rate.values())
rate2=list(death_rate.values())
rate3=list(natural_growth_rate.values())
d={'出生率   ':rate1,                   #将数值带入
   '死亡率     ':rate2,
   '自然增长率         ':rate3}
df=pd.DataFrame(d,index=years)    #将年份和比率结合一起
print(df)                   #输出结果

3.文本分析(可选):因为都是数字,所以这里没有用到文本分析

4.数据分析与可视化:

(1)折线图

years=years[::-1]  #因为后面用到的数据都是从左到右年份逐渐增大,所以数据全部都需要倒一下
rate1=rate1[::-1]
rate2=rate2[::-1]
rate3=rate3[::-1]
#print(years)

#绘制折线图
x=years   #设置x轴
plt.figure(figsize=(12,8),dpi=80)   #设置绘制的图像和字体大小
plt.plot(x,rate1,color = 'r',label="birth_rate")#r是红色
plt.plot(x,rate2,color = 'y',label="death_rate")#k是黄色
plt.plot(x,rate3,color = 'b',label="natural_growth_rate")#b是蓝色
plt.xlabel("year")#横坐标名字
plt.ylabel("rate")#纵坐标名字
plt.legend(loc = "best")#图例
plt.show()   

(2)柱状图

#绘制柱状图
plt.figure(figsize=(12,8),dpi=80)   #设置绘制的图像和字体大小
x = np.arange(21) #总共有几组,就设置成几,我们这里有21组,所以设置为21
total_width, n = 0.8, 3    
# 有多少个类型,只需更改n即可,比如这里我们对比了3个,那么就把n设成3
width = total_width / n
x = x - (total_width - width) / 2
plt.bar(x, rate1, color = "r",width=width,label='birth_rate')
plt.bar(x + width, rate2, color = "y",width=width,label='death_rate')
plt.bar(x + 2 * width,rate3 , color = "c",width=width,label='natural_growth_rate')
plt.xlabel("year")
plt.ylabel("rate")
plt.legend(loc = "best")
plt.xticks([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21],years) 
#这里之所以一个个打,因为快一点
my_y_ticks = np.arange(0, 20, 2)
plt.ylim((0, 20))
plt.yticks(my_y_ticks)
plt.show()

(3)直方图

#绘制直方图
plt.figure(figsize=(12,8),dpi=80)
plt.hist(rate1,bins=40, normed=0, facecolor="blue", edgecolor="blue", alpha=0.7) 
#bins是直方图的长条形数目
plt.hist(rate2,bins=40, normed=0, facecolor="yellow", edgecolor="yellow", alpha=0.7)
#normed是否将得到的直方图向量归一化
plt.hist(rate3,bins=40, normed=0, facecolor="green", edgecolor="green", alpha=0.7)
plt.xlabel("比率")
plt.ylabel("总数")
plt.title("人口比率直方图")
plt.show()    #蓝色是出生率,黄色是死亡率,绿色是自然增长率

(4)堆叠条形图

#绘制堆叠条形图
plt.figure(figsize=(12,8),dpi=80)
x=range(len(rate1))
rects1 = plt.bar(x,height=rate1, width=0.45, alpha=0.8, color='red', label="出生率")
rects2 = plt.bar(x,height=rate2, width=0.45, color='green', label="死亡率")
#x表示条数 height表示高度 
plt.ylim(0, 20)
plt.ylabel("比率")
plt.xticks(x, years)
plt.xlabel("年份")
plt.title("人口出生率与死亡率对比")
plt.legend()
plt.show()

(5)横向柱状图

#绘制横向的柱状图
plt.figure(figsize=(12,8),dpi=80)
a=0.3   #设置分开的间隔
index = np.arange(1999,2020)
plt.barh(index,rate1, a, color = 'brown', label = 'birth_rate')
plt.barh(index+a, rate2, a, color = 'pink', label = 'death_rate')
plt.barh(index+2*a, rate3, a, color = 'orange',label = 'natural_growth_rate')
plt.legend()

(6)饼图

#绘制饼图
plt.figure(figsize=(12,8),dpi=80)
label_list = ["birth_rate", "death_rate", "natural_growth_rate"]    # 各部分标签
size = [33, 33, 33]    # 各部分大小
color = ["red", "green", "blue"]
explode = [0.05, 0, 0]   # 各部分突出值
patches, l_text, p_text = plt.pie(size, explode=explode, colors=color,\
                                  labels=label_list, labeldistance=1.1,\
                                  autopct="%1.1f%%", shadow=False, startangle=90, pctdistance=0.6)
plt.axis("equal")    # 设置横轴和纵轴大小相等,这样饼才是圆的
plt.legend()
plt.show() #因为年份,出现的次数都是相等,所以这个图三部分都一样

(7)子图

#绘制子图
plt.figure(figsize=(12,8),dpi=80) # 创建figure对象
# 绘制子图1
axes1 = plt.subplot(2,2,1)
plt.plot(years,rate1,'r-o')
plt.title('birth_rate')
# 绘制子图2
axes2 = plt.subplot(2,2,2)
plt.plot(years,rate2,'g-*')
plt.title('death_rate')
# 绘制子图3
axes3 = plt.subplot(2,1,2)
plt.plot(years,rate3,'y-1')
plt.title('natural_growth_rate')

(8)三维图

#绘制3D图
fig=plt.figure(figsize=(10,8),dpi=80)
ax = Axes3D(fig)
ax.scatter3D(rate1,rate2,rate3, cmap='Blues')  #绘制散点图  
ax.plot3D(rate1,rate2,rate3,'gray')    #绘制空间曲线
#因为人口的出生率死亡率和自然增长率都极为接近,所以结果看着像一条直线
plt.show()

(9)三维曲面图

#绘制三维曲面
fig = plt.figure(figsize=(14,8),dpi=80)  #定义新的三维坐标轴
ax3 = plt.axes(projection='3d')
ratel1=[]  
ratel2=[]
ratel3=[]
for i in rate1:    #这里因为不是整数就运行不了,所以将浮点数类型变成整数类型
    ratel1.append(int(i))
for i in rate2:
    ratel2.append(int(i))
for i in rate3:
    ratel3.append(int(i))
xx = ratel1  #这样能更清晰的知道X,Y,Z轴
yy = ratel2
zz = ratel3
X, Y = np.meshgrid(xx, yy)
Z = np.sin(X)+np.cos(Y) +2
ax3.plot_surface(X,Y,Z,cmap='rainbow')
#x代表出生率,y代表死亡率,z代表自然增长率
plt.show()

5.根据数据之间的关系,分析两个变量之间的相关系数,画出散点图,并建立变量之间的回归方程

(1)年份与出生率

#年份与出生率之间的相关系数,以及散点图
yearl=[]
years=years[::-1]   #因为df出生率是正的,而之前年份倒过来过,所以这里需要倒一次
for i in years:
    yearl.append(float(i))
year = pd.Series(yearl) #利用Series将列表转换成新的、pandas可处理的数据
birth_rate = pd.Series(rate1)
corr_gust = round(year.corr(birth_rate), 4) #计算标准差,round(a, 4)是保留a的前四位小数
print('年份与出生率之间的相关系数:', corr_gust)
plt.figure(figsize=(12,8),dpi=80)
sns.stripplot(x =years,y = '出生率   ',data = df)

(2)出生率与死亡率

#出生率与死亡率之间的相关系数,以及散点图
birth_rate=pd.Series(rate1)
death_rate=pd.Series(rate2)
corr_gust = round(birth_rate.corr(death_rate), 4)
print('出生率与死亡率之间的相关系数:', corr_gust)
y=df[["出生率   "]]
x=df[["死亡率     "]]
pd.plotting.scatter_matrix(
    df[["出生率   ", "死亡率     "]],
    alpha=0.8, figsize=(10, 10), diagonal='kde'
)
plt.show()

#建立出生率与死亡率的一元非线性回归方程以及图示
df1=df[["出生率   ","死亡率     "]]
sns.lmplot(x="出生率   ",y="死亡率     ",data=df1)

6.数据持久化

f1 = open('pickle.txt','wb')   #建立一个文件,以二进制形式
pickle.dump(df,f1)         # pickle.dump(obj, file, [,protocol]) file为对象保存到的类文件对象
f1.flush()           #flush是用来刷新缓冲区的
f1.close()
read_file = open('pickle.txt','rb')
data = pickle.load(read_file)    #从read_file(文件对象)中读取一个字符串,并将它重构为原来的python对象。
print(data)
read_file.close()

7.将以上各部分的代码汇总,附上完整程序代码

import requests
import pandas as pd
import json
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
import pickle

matplotlib.rcParams['font.sans-serif']=['SimHei']  
# 用黑体显示中文 
matplotlib.rcParams['axes.unicode_minus']=False     
# 正常显示负号
dfwds = '[{"wdcode": "sj", "valuecode": "LAST21"}, {"wdcode":"zb","valuecode":"A0302"}]'
#网址的dfwds为上面所写的,所以更快的获取要的数据
url='http://data.stats.gov.cn/easyquery.htm?m=QueryData&dbcode=hgnd&rowcode=sj&colcode=zb&wds=[]&dfwds={}'
#获取url
response = requests.get(url.format(dfwds)) #获取网页的数据
z=response.json()     #获取网址的json
z=z['returndata']     #通过字典的key值,获取数据
z=z['datanodes']
#print(z)
birth_rate={}    #分别设置三个字典,用来装后面的值
death_rate={}
natural_growth_rate={}
for i in range(len(z)//3):     #通过循环,获取结果
    #print("年份{}:出生率{}".format(z[i]['wds'][1]['valuecode'],z[i]['data']['data']))
    birth_rate[z[i]['wds'][1]['valuecode']]=z[i]['data']['data']
#print(birth_rate)
for i in range(len(z)//3,len(z)//3*2):
    #print("年份{}:死亡率{}".format(z[i]['wds'][1]['valuecode'],z[i]['data']['data']))
    death_rate[z[i]['wds'][1]['valuecode']]=z[i]['data']['data']
#print(death_rate)
for i in range(len(z)//3*2,len(z)):
    #print("年份{}:自然增长率{}".format(z[i]['wds'][1]['valuecode'],z[i]['data']['data']))
    natural_growth_rate[z[i]['wds'][1]['valuecode']]=z[i]['data']['data']
#print(natural_growth_rate)
years=list(birth_rate.keys())   #把字典里面的key和value都拿出来,分别转成列表类型
rate1=list(birth_rate.values())
rate2=list(death_rate.values())
rate3=list(natural_growth_rate.values())
d={'出生率   ':rate1,                   #将数值带入
   '死亡率     ':rate2,
   '自然增长率         ':rate3}
df=pd.DataFrame(d,index=years)    #将年份和比率结合一起
f1 = open('pickle.txt','wb')
pickle.dump(df,f1)
f1.flush()
f1.close()
read_file = open('pickle.txt','rb')
data = pickle.load(read_file)
#print(data)
read_file.close()
#print(df)                   #输出结果
years=years[::-1]  #因为后面用到的数据都是从左到右年份逐渐增大,所以数据全部都需要倒一下
rate1=rate1[::-1]
rate2=rate2[::-1]
rate3=rate3[::-1]
#print(years)

#绘制折线图
x=years   #设置x轴
plt.figure(figsize=(12,8),dpi=80)   #设置绘制的图像和字体大小
plt.plot(x,rate1,color = 'r',label="birth_rate")#r是红色
plt.plot(x,rate2,color = 'y',label="death_rate")#k是黄色
plt.plot(x,rate3,color = 'b',label="natural_growth_rate")#b是蓝色
plt.xlabel("year")#横坐标名字
plt.ylabel("rate")#纵坐标名字
plt.legend(loc = "best")#图例
plt.show()   

#绘制柱状图
plt.figure(figsize=(12,8),dpi=80)   #设置绘制的图像和字体大小
x = np.arange(21) #总共有几组,就设置成几,我们这里有21组,所以设置为21
total_width, n = 0.8, 3    
# 有多少个类型,只需更改n即可,比如这里我们对比了3个,那么就把n设成3
width = total_width / n
x = x - (total_width - width) / 2
plt.bar(x, rate1, color = "r",width=width,label='birth_rate')
plt.bar(x + width, rate2, color = "y",width=width,label='death_rate')
plt.bar(x + 2 * width,rate3 , color = "c",width=width,label='natural_growth_rate')
plt.xlabel("year")
plt.ylabel("rate")
plt.legend(loc = "best")
plt.xticks([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21],years) 
#这里之所以一个个打,因为快一点
my_y_ticks = np.arange(0, 20, 2)
plt.ylim((0, 20))
plt.yticks(my_y_ticks)
plt.show()

#绘制直方图
plt.figure(figsize=(12,8),dpi=80)
plt.hist(rate1,bins=40, normed=0, facecolor="blue", edgecolor="blue", alpha=0.7) 
#bins是直方图的长条形数目
plt.hist(rate2,bins=40, normed=0, facecolor="yellow", edgecolor="yellow", alpha=0.7)
#normed是否将得到的直方图向量归一化
plt.hist(rate3,bins=40, normed=0, facecolor="green", edgecolor="green", alpha=0.7)
plt.xlabel("比率")
plt.ylabel("总数")
plt.title("人口比率直方图")
plt.show()    #蓝色是出生率,黄色是死亡率,绿色是自然增长率
'
#绘制堆叠条形图
plt.figure(figsize=(12,8),dpi=80)
x=range(len(rate1))
rects1 = plt.bar(x,height=rate1, width=0.45, alpha=0.8, color='red', label="出生率")
rects2 = plt.bar(x,height=rate2, width=0.45, color='green', label="死亡率")
#x表示条数 height表示高度 
plt.ylim(0, 20)
plt.ylabel("比率")
plt.xticks(x, years)
plt.xlabel("年份")
plt.title("人口出生率与死亡率对比")
plt.legend()
plt.show()

#绘制横向的柱状图
plt.figure(figsize=(12,8),dpi=80)
a=0.3   #设置分开的间隔
index = np.arange(1999,2020)
plt.barh(index,rate1, a, color = 'brown', label = 'birth_rate')
plt.barh(index+a, rate2, a, color = 'pink', label = 'death_rate')
plt.barh(index+2*a, rate3, a, color = 'orange',label = 'natural_growth_rate')
plt.legend()

#绘制饼图
plt.figure(figsize=(12,8),dpi=80)
label_list = ["birth_rate", "death_rate", "natural_growth_rate"]    # 各部分标签
size = [33, 33, 33]    # 各部分大小
color = ["red", "green", "blue"]
explode = [0.05, 0, 0]   # 各部分突出值
patches, l_text, p_text = plt.pie(size, explode=explode, colors=color,\
                                  labels=label_list, labeldistance=1.1,\
                                  autopct="%1.1f%%", shadow=False, startangle=90, pctdistance=0.6)
plt.axis("equal")    # 设置横轴和纵轴大小相等,这样饼才是圆的
plt.legend()
plt.show() #因为年份,出现的次数都是相等,所以这个图三部分都一样

#绘制子图
plt.figure(figsize=(12,8),dpi=80) # 创建figure对象
# 绘制子图1
axes1 = plt.subplot(2,2,1)
plt.plot(years,rate1,'r-o')
plt.title('birth_rate')
# 绘制子图2
axes2 = plt.subplot(2,2,2)
plt.plot(years,rate2,'g-*')
plt.title('death_rate')
# 绘制子图3
axes3 = plt.subplot(2,1,2)
plt.plot(years,rate3,'y-1')
plt.title('natural_growth_rate')

#绘制3D图
fig=plt.figure(figsize=(10,8),dpi=80)
ax = Axes3D(fig)
ax.scatter3D(rate1,rate2,rate3, cmap='Blues')  #绘制散点图  
ax.plot3D(rate1,rate2,rate3,'gray')    #绘制空间曲线
#因为人口的出生率死亡率和自然增长率都极为接近,所以结果看着像一条直线
plt.show()

#绘制三维曲面
fig = plt.figure(figsize=(14,8),dpi=80)  #定义新的三维坐标轴
ax3 = plt.axes(projection='3d')
ratel1=[]  
ratel2=[]
ratel3=[]
for i in rate1:    #这里因为不是整数就运行不了,所以将浮点数类型变成整数类型
    ratel1.append(int(i))
for i in rate2:
    ratel2.append(int(i))
for i in rate3:
    ratel3.append(int(i))
xx = ratel1  #这样能更清晰的知道X,Y,Z轴
yy = ratel2
zz = ratel3
X, Y = np.meshgrid(xx, yy)
Z = np.sin(X)+np.cos(Y) +2
ax3.plot_surface(X,Y,Z,cmap='rainbow')
#x代表出生率,y代表死亡率,z代表自然增长率
plt.show()

#年份与出生率之间的相关系数,以及散点图
yearl=[]
years=years[::-1]   #因为df出生率是正的,而之前年份倒过来过,所以这里需要倒一次
for i in years:
    yearl.append(float(i))
year = pd.Series(yearl) #利用Series将列表转换成新的、pandas可处理的数据
birth_rate = pd.Series(rate1)
corr_gust = round(year.corr(birth_rate), 4) #计算标准差,round(a, 4)是保留a的前四位小数
print('年份与出生率之间的相关系数:', corr_gust)
plt.figure(figsize=(12,8),dpi=80)
sns.stripplot(x =years,y = '出生率   ',data = df)

#出生率与死亡率之间的相关系数,以及散点图
birth_rate=pd.Series(rate1)
death_rate=pd.Series(rate2)
corr_gust = round(birth_rate.corr(death_rate), 4)
print('出生率与死亡率之间的相关系数:', corr_gust)
y=df[["出生率   "]]
x=df[["死亡率     "]]
pd.plotting.scatter_matrix(
    df[["出生率   ", "死亡率     "]],
    alpha=0.8, figsize=(10, 10), diagonal='kde'
)
plt.show()

#建立出生率与死亡率的一元非线性回归方程以及图示
df1=df[["出生率   ","死亡率     "]]
sns.lmplot(x="出生率   ",y="死亡率     ",data=df1)

四、结论

1.经过对主题数据的分析与可视化得到的结论:通过对数据的分析以及可视化可以看出,近几十年来我国的出生率逐渐降低,死亡率逐渐升高,自然增长率也因此逐年下降

2.对本次程序设计任务完成的情况做一个简单的小结:通过这次爬虫,然后对数据的分析可视化等,让我了解到了数据的多种用途,

以及可以通过图像的方式显示出来,和不同数据之间的相关性等,让我学到了很多

猜你喜欢

转载自www.cnblogs.com/luoi/p/12633918.html