前言
近段时间经常收到招商银行关于理财相关的短信,看来我已成了招行的目标客户(虽然我还很穷~)。穷归穷,看看还是不花钱的,于是前往招行的理财产品网站看看,嚯!足足上千种理财产品,让我迸发了了解数据的冲动。于是乎,按照分析师的习惯,先从网站抓取数据,然后基于获得的数据作一个探究。
数据爬虫
当你来到对方的理财产品网站,你会发现网页中的数据并没有直接的存储在网页源代码中,因为当我不停的下拉,并点击下一页时,其链接并没有发生变化。OK,那这样看来,数据一定是作了异步存储,接下来找到这个异步存储数据的链接才是最关键的。下面,把寻找目标链接的方法作一一说明:
使用Chrome浏览器,打开链接:http://www.cmbchina.com/cfweb/personal/default.aspx
按F12键,选择“Network”选项,并点击网页中的下一页
在“XHR”中你会出现一个文件,去点击它
选择“Preview”预览,发现数据
双击“XHR”中的文件,得到数据链接
接下来要做的就是,发现链接中的规律,调整链接中对应的“pageindex”,得到不同页下的数据,具体爬虫代码如下:
# ===== Python3 =====# 导入第三方包
import requests
import re
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
# 设置请求头
headers = {'Accept':'*/*','Accept-Encoding':'gzip, deflate','Accept-Language':'zh-CN,zh;q=0.9','Connection':'keep-alive','User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'
}
# 拼接URL,用于翻页爬虫
url_phase1 = 'http://www.cmbchina.com/cfweb/svrajax/product.ashx?op=search&type=m&pageindex='
url_phase2 = '&salestatus=&baoben=¤cy=&term=&keyword=&series=01&risk=&city=&date=&pagesize=40&orderby=ord1&t=0.8683289736280901'
urls = []for i in range(1,29):
urls.append(url_phase1+str(i)+url_phase2)
# 构造空列表,用于后面的数据存储
Finacing = []
# 通过for循环完成URL的遍历
for url in urls:
# 获取源代码
res = requests.get(url, headers = headers).text
# 正则表达式完成信息的获取
ProdCode = re.findall('PrdCode:"(.*?)",',text)
ProdName = re.findall('PrdName:"(.*?)",',text)
TypeCode = re.findall('TypeCode:"(.*?)",',text)
AreaCode = re.findall('AreaCode:"(.*?)",',text)
BeginDate = re.findall('BeginDate:"(.*?)",',text)
EndDate = re.findall('EndDate:"(.*?)",',text)
ExpireDate = re.findall('ExpireDate:"(.*?)",',text)
Status = re.findall('Status:"(.*?)",',text)
NetValue = re.findall('NetValue:"(.*?)",',text)
IsNewFlag = re.findall('IsNewFlag:"(.*?)",',text)
NetValue = re.findall('NetValue:"(.*?)",',text)
Term = re.findall('Term:"(.*?)",',text)
Style = re.findall('Style:"(.*?)",',text)
InitMoney = re.findall('InitMoney:"(.*?)",',text)
IncresingMoney = re.findall('IncresingMoney:"(.*?)",',text)
Risk = re.findall('Risk:"(.*?)",',text)
FinDate = re.findall('FinDate:"(.*?)",',text)
SaleChannel = re.findall('SaleChannel:"(.*?)",',text)
SaleChannelName = re.findall('SaleChannelName:"(.*?)",',text)
IsCanBuy = re.findall('IsCanBuy:"(.*?)"}',text)
# 数据存储到字典中
Finacing.append({'ProdCode':ProdCode,'ProdName':ProdName,'TypeCode':TypeCode,'AreaCode':AreaCode,
'BeginDate':BeginDate,'EndDate':EndDate,'ExpireDate':ExpireDate,'Status':Status,
'NetValue':NetValue,'IsNewFlag':IsNewFlag,'NetValue':NetValue,'Term':Term,
'Style':Style,'InitMoney':InitMoney,'IncresingMoney':IncresingMoney,'Risk':Risk,
'FinDate':FinDate,'SaleChannel':SaleChannel,'SaleChannelName':SaleChannelName,'IsCanBuy':IsCanBuy})
# 睡眠3秒
time.sleep(3)
# 将数据转换为数据框
CMB_Finance = pd.concat([pd.DataFrame(data) for data in Finacing])
# 数据导出
CMB_Finance.to_excel('CMB_Finance.xlsx', index = False)
很快就可以把招商银行网站中的1000多种理财产品全部获取回来。接下来要做的就是数据的探索性分析,了解数据的特征和分布。
探索性分析
如果你没有完成上面的爬虫也没有关系,本文后面提供了数据集下载的链接,你可以通过读取的方式进行这部分的探索性分析。
数据类型转换
# 读取数据
cmb = pd.read_excel('CMB_Finance.xlsx')
# 查看数据集的行列数
cmb.shape
# 查看数据前几行
cmb.head()
# 查看数据集的数据类型c
mb.dtypes
从爬下来的数据表来看,除了InitMoney(最低投资额)、IncresingMoney(投资增加额)和TypeCode(产品类型编码)是数值型的,其他都是字符串型。这里,我们是可以把FinDate(期限)和NetValue(收益率)字段转换为数值型的。
# 数据类型转换# 将FinDate(期限)字段转换为数值型
cmb.FinDate = cmb.FinDate.str[:-1].astype('int')
# 将NetValue(收益率)字段转换为数值型
cmb.NetValue = cmb.NetValue.str[:-1].astype('float')/100
收益率最高和最低的3种产品
对于大部分理财用户来说,都会非常关心理财产品的收益率,虽然都知道高风险高收益,但还是会权衡收益率最高的产品和最低的产品。接下来,我们借助于条形图来展示收益率:
# 预期收益率最高的3个产品
NetValue_sort_desc = cmb[['ProdCode', 'NetValue']].sort_values(by = 'NetValue', ascending=False)
NetValue_duplicate_top = NetValue_sort_desc.drop_duplicates(subset = 'NetValue').head(3)
# 预期收益率最低的3个产品
NetValue_sort_asc = cmb[['ProdCode', 'NetValue']].sort_values(by = 'NetValue', ascending=True)
NetValue_duplicate_last = NetValue_sort_asc.drop_duplicates(subset = 'NetValue').head(3)
# 中文和负号的正常显示
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
# 设置图形的显示风格
plt.style.use('ggplot')
# 为了让多张子图在一张图中完成,设置子图的位置
ax1 = plt.subplot2grid((2,1),(0,0))
ax2 = plt.subplot2grid((2,1),(1,0))
# 绘制条形图
ax1.bar(range(3), NetValue_duplicate_top.NetValue, align = 'center', color = 'steelblue', alpha = 0.7)
# y轴范围
ax1.set_ylim(0.045,0.051)
# x轴刻度标签
ax1.set_xticks(np.arange(3))
ax1.set_xticklabels(NetValue_duplicate_top.ProdCode)
# x轴标签
ax1.set_xlabel('产品编号')
# y轴刻度标签
ax1.set_yticks(np.arange(0.045,0.051,0.001))
ax1.set_yticklabels([str(i*100) + '%' for i in np.arange(0.045,0.051,0.001)])
# y轴标签
ax1.set_ylabel('预期收益率')
# 标题
ax1.set_title('预期收益率最高的5类产品')
ax2.bar(range(3), NetValue_duplicate_last.NetValue, align = 'center',color = 'steelblue', alpha = 0.7)
ax2.set_ylim(0.045,0.048)
ax2.set_xticks(np.arange(3))
ax2.set_xticklabels(NetValue_duplicate_last.ProdCode)
# x轴标签
ax2.set_xlabel('产品编号')
# y轴刻度标签
ax2.set_yticks(np.arange(0.045,0.048,0.001))
ax2.set_yticklabels([str(i*100) + '%' for i in np.arange(0.045,0.048,0.001)])
# y轴标签
ax2.set_ylabel('预期收益率')
ax2.set_title('预期收益率最低的5类产品')
# 调整子图之间的高度间距
plt.subplots_adjust(hspace=0.5)
# 去除图形顶部边界和右边界的刻度
plt.tick_params(top='off', right='off')
# 图形显示
plt.show()
图中显示,最高的收益率可以达到5%,最低的收益率为4.6%(肯定都比存银行的收益率都高吧~)。
理财产品风险类型分布
从表中我们得知,这1000多种产品分为三种风险类型,分别是谨慎性、稳健性和平稳性,他们之间的比例如何呢?我们通过饼图来呈现这个比例:
# 先对各风险类型的样本量作统计
stats = cmb.Risk.value_counts()
plt.axes(aspect='equal')
# 控制x轴和y轴的范围
plt.xlim(0,4)
plt.ylim(0,4)
explode = [0,0,0.1,]
colors=['#9999ff','#ff9999','#7777aa'] # 自定义颜色
# 绘制饼图
plt.pie(x = stats.values, # 绘图数据
explode=explode, # 突出显示谨慎性产品
labels=stats.index, # 添加教育水平标签
colors=colors, # 设置饼图的自定义填充色
autopct='%.1f%%', # 设置百分比的格式,这里保留一位小数
pctdistance=0.8, # 设置百分比标签与圆心的距离
labeldistance = 1.15,
startangle = 180, # 设置饼图的初始角度
radius = 1.5, # 设置饼图的半径
counterclock = False, # 是否逆时针,这里设置为顺时针方向
wedgeprops = {'linewidth': 1.5, 'edgecolor':'green'},# 设置饼图内外边界的属性值
textprops = {'fontsize':12, 'color':'k'}, # 设置文本标签的属性值
center = (1.8,1.8), # 设置饼图的原点
frame = 1 )# 是否显示饼图的图框,这里设置显示
# 删除x轴和y轴的刻度
plt.xticks(())
plt.yticks(())
# 添加图标题
plt.title('理财产品风险类型分布')
# 显示图形
plt.show()
上图显示,谨慎性的产品是最少的,只有10%。这个也比较容易理解,毕竟银行也是想从更多的风险投资人那边获得更多的利益,而谨慎性的产品(收益率不会高,并且是浮动收益)应该是用来起吆喝,吸流量的作用(毕竟大部分人还是停留在保值的心理阶段)。
理财产品期限的描述性统计
接下来我们来了解一下,这些理财产品的期限FinDate,上面已经将该变量转换为数值型了,首先对改变了作一下描述性统计:
# 理财产品期限的描述性统计
cmb.FinDate.describe()
我们发现,这些理财产品的平均期限为半年(180天),而且四分之三的产品也是在180天以内。我想这应该是招商银行做过统计,例如,什么样的理财产品好卖,这些产品在消费者手中的持有时间都是多少天等。同时,我们也可以针对这个期限作一个直方图,来看看时间上的分布特征:
# 理财产品期限的直方图
plt.hist(cmb.FinDate, # 绘图数据
bins = np.arange(cmb.FinDate.min(),cmb.FinDate.max(),30), # 指定直方图的组距
normed = True, # 设置为频率直方图
color = 'steelblue', # 指定填充色
edgecolor = 'k') # 指定直方图的边界色
# 设置坐标轴标签和标题
plt.title('理财产品期限直方图')
plt.xlabel('期限(天数)')
plt.ylabel('频率')
# 生成正态曲线的数据
x1 = np.linspace(cmb.FinDate.min(), cmb.FinDate.max(), 1000)
normal = mlab.normpdf(x1, cmb.FinDate.mean(), cmb.FinDate.std())
# 绘制正态分布曲线
line1, = plt.plot(x1,normal,'', linewidth = 2)
# 生成核密度曲线的数据
kde = mlab.GaussianKDE(cmb.FinDate)
x2 = np.linspace(cmb.FinDate.min(), cmb.FinDate.max(), 1000)
# 绘制
line2, = plt.plot(x2,kde(x2),'', linewidth = 2)
# 去除图形顶部边界和右边界的刻度
plt.tick_params(top='off', right='off')
# 显示图例
plt.legend([line1, line2],['正态分布曲线','核密度曲线'],loc='best')
# 显示图形
plt.show()
不错所料,期限呈现严重的右偏特征,这也是绝大多数经济活动的共性,如消费频次越多的人肯定越少;花费金额越多的人也肯定越少。
基于风险类型的期限分布
我们可以进一步细化,看看不同类型风险的产品,是不是产品期限存在明显的差异,这里我们就借助于箱线图做一个比较:
# 如果将其划分到不同的Risk(风险类型)中,期限的分布是否存在差异
FinDate = []
Risks = cmb.Risk.unique()
Risks.sort()
for Risk in Risks:
FinDate.append(cmb.loc[cmb.Risk==Risk,'FinDate'])
# 绘图
plt.boxplot(x = FinDate,
patch_artist=True,
labels = Risks, # 添加具体的标签名称
showmeans=True,
boxprops = {'color':'black','facecolor':'#9999ff'},
flierprops = {'marker':'o','markerfacecolor':'red','color':'black'},
meanprops = {'marker':'D','markerfacecolor':'indianred'},
medianprops = {'linestyle':'--','color':'orange'})
# 设置坐标轴标签和标题
plt.title('不同风险类型下的产品期限差异盒形图图')
plt.xlabel('风险类型')
plt.ylabel('期限')
# 去除图形顶部边界和右边界的刻度
plt.tick_params(top='off', right='off')
# 显示图形
plt.show()
图中显示,不同风险类型的理财产品确实在期限上存在差异,对于平衡性的产品,平均期限在500天左右,而稳健性和谨慎性的平均期限只有110天和170天。
结语
OK,今天关于数据爬虫和数据的探索性分析就分享到这里,希望感兴趣的朋友能够照着做一遍,相信对你一定有帮助。如果你有任何问题,欢迎在公众号的留言区域表达你的疑问。同时,也欢迎各位朋友继续转发与分享文中的内容,让更多的人学习和进步。
关注“每天进步一点点2015”,与小编同进步!
文内代码及数据
链接: https://pan.baidu.com/s/1mi1hUtE 密码: ya9v