往期经典回顾
前言
近段时间经常收到招商银行关于理财相关的短信,看来我已成了招行的目标客户(虽然我还很穷~)。穷归穷,看看还是不花钱的,于是前往招行的理财产品网站看看,嚯!足足上千种理财产品,让我迸发了了解数据的冲动。于是乎,按照分析师的习惯,先从网站抓取数据,然后基于获得的数据作一个探究。
数据爬虫
当你来到对方的理财产品网站,你会发现网页中的数据并没有直接的存储在网页源代码中,因为当我不停的下拉,并点击下一页时,其链接并没有发生变化。OK,那这样看来,数据一定是作了异步存储,接下来找到这个异步存储数据的链接才是最关键的。下面,把寻找目标链接的方法作一一说明:
使用Chrome浏览器,打开链接:http://www.cmbchina.com/cfweb/personal/default.aspx

按F12键,选择“Network”选项,并点击网页中的下一页

在“XHR”中你会出现一个文件,去点击它
选择“Preview”预览,发现数据

双击“XHR”中的文件,得到数据链接

接下来要做的就是,发现链接中的规律,调整链接中对应的“pageindex”,得到不同页下的数据,具体爬虫代码如下:
# ===== Python3 =====# 导入第三方包import requestsimport reimport timeimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport 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()# 查看数据集的数据类型cmb.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,'r-', linewidth = 2)
# 生成核密度曲线的数据kde = mlab.GaussianKDE(cmb.FinDate)
x2 = np.linspace(cmb.FinDate.min(), cmb.FinDate.max(), 1000)# 绘制line2, = plt.plot(x2,kde(x2),'g-', 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
本文通过爬虫技术抓取并分析了招商银行理财产品数据,揭示了不同风险等级产品在收益率、期限等方面的特性。
&spm=1001.2101.3001.5002&articleId=98764614&d=1&t=3&u=df63f796a6c44258beae81a492ed774d)
2752

被折叠的 条评论
为什么被折叠?



