租房分期产品用户风险识别与分析
主要内容
本案例主要从背景、数据抓取、数据分析和结论四个部分进行介绍租房分期产品好坏用户识别与分析。
背景介绍
租房分期产品是基于租赁场景下,根据租客资质给予一笔消费额度,旨在解决租客分期支付房租的问题,帮助缓解租客的生活压力。自租房分期产品上线以来,有效缓解租客支付房租压力,深受广大租客用户的喜爱。由于各行各业的租客资质水平参差不齐,加之产品上线时间不长,还款期限最长12个月,那么就会存在一些租客在贷前、贷中、贷后存在一定潜在的风险。为了尽快获得产品用户的还款能力和还款意愿等情况,通过租客过往数据识别好坏用户尤为必要。这里的“好用户”指提前还款、按时还款的用户,而“坏用户”指有逾期还款的用户。那么我们该如何找出坏用户进行控制和惩罚,又该如何筛选出好用户进行增信和奖励呢?某网站上有我们需要的信息,于是采用网络爬虫的方法抓取目标网页信息来鉴别出“好坏用户”。因保护企业隐私,暂不对该网站做任何介绍,同时对爬取的数据字段也会做适当的修改,但不会影响方法效果和数据分析之后得出的结果,仅从解决问题思路与方法的角度进行分享,禁止商业使用,特此声明。
数据抓取
网页数据抓取的主要分析思路如下。
1.了解一下网络连接远离和爬虫原理,方便大家理解网页爬取!
对于爬取网页数据,我们只需要知道最基本的网络原理即可,不要因HTML、JavaScript、CSS、HTML5等前端知识把爬虫问题想成超级难的事情,但学好它也并非易事,因为目标网页结构和机制不同,面临的实际问题难易程度也就不同。
好了,废话不说,直接说重点。当计算机一次requests请求和服务器端的response回应也就成功实现了一次网络连接。而爬虫也是需要做两件事:(1)模拟计算机对服务器发起的请求。(2)接受服务器端的response内容并解析、提取所需的信息。
2.知道用哪些工具爬取网页数据。
那么问题来了,如何对服务器发出请求,又如何接收、解析并提取服务器返回的网页内容呢?下面我给大家介绍Python的2个常用的网络爬虫库,一个是requests,另一个是BeautifulSoup。Requests库就是请求网站,获取网站源代码的。BeautifulSoup能够把requests库返回的源代码解析成soup文档,便于后面使用BeautifulSoup库中内置函数和方法定位并提取所需要的元素。
3.熟悉主要的爬虫流程
主要的爬虫流程有多页面爬虫流程和跨页面爬虫流程,下面分别介绍一下这两个爬虫流程。
多页面的爬虫流程是基于每页的页面结构都相同或者类似,方便构建URL翻页操作。具体流程步骤为:
- 打开谷歌浏览器,通过快捷键ctrl+shif+I 和 ctrl+u查看网页构造和网页信息,便于观察各个网页的URL构成特点,并把构造出的URL存入列表。
- 根据URL列表依次循环取出URL
- 定义爬虫函数。
- 循环调用爬虫函数,存储数据。
- 循环完毕,结束爬虫程序。
当网页有列表页和详情页的时候,就需要用到跨页面爬虫流程了。具体流程如下:
- 定义爬虫函数爬取所有的URL,并将其存入列表中。
- 定义爬取详情页数据函数。
- 进入详情页爬取详情页数据。
- 存储数据,循环完毕,结束爬虫。
既然我们已经知道抓取网页原理与工具,以及爬虫流程,下面我们就可以爬取网页数据。
利用head()预览刚才爬取的数据,然后使用info()了解数据类型。
// 使用pandas中head()预览抓取的数据
import pandas as pd
df = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata.xlsx",sheet_name = "Sheet1"
#,index_col=0
,usecols=[6,7,8,9,10,11,12,13,14,15,16])
df.head(10)
输出结果
仅展示部分:
现居住地址 | 订单编号 | 订单状态 | 下单时间 | 借款金额 | 借款期限 | 应还款日 | 应还款总金额 | 本金 | 利息 |
---|---|---|---|---|---|---|---|---|---|
NaN | 171122037056 | 逾期已还款 | 2017-11-22 10:50:00 | 1000.0 | 14 | 2017-12-05 11:03:00 | 6333.6 | 1000.0 | 13.6 |
山西省 太原市 万柏林区 小井裕街华峪东区 | YQG011712060654316780 | 逾期已还款 | 2017-12-06 06:54:00 | 1000.0 | 14 | 2017-12-19 10:02:00 | 6333.6 | 1000.0 | 13.6 |
NaN | YQG011808240013233570 | 申请中 | 2018-08-24 12:13:00 | 1000.0 | 14 | 2018-09-07 12:13:00 | 1013.6 | 1000.0 | 13.6 |
// 使用info()了解数据类型
import pandas as pd
df = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata.xlsx",sheet_name = "Sheet1"
#,index_col=0
,usecols=[6,7,8,9,10,11,12,13,14,15,16])
df.info()
输出结果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 17099 entries, 0 to 17098
Data columns (total 10 columns):
现居住地址 9635 non-null object
订单编号 17099 non-null object
订单状态 17099 non-null object
下单时间 17099 non-null datetime64[ns]
借款金额 17099 non-null float64
借款期限 17099 non-null int64
应还款日 17099 non-null datetime64[ns]
应还款总金额 17099 non-null float64
本金 17099 non-null float64
利息 17099 non-null float64
dtypes: datetime64[ns](2), float64(4), int64(1), object(3)
memory usage: 1.3+ MB
通过info()方法可以看出展示的数据总共10个字段,17099条,现居住地址有空值,可以单独列出做分析。
使用matplotlib.pyplot绘制箱线图描述数值分布情况
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#然图表直接在Jupyter Notebook中展示出来
%matplotlib inline
#解决中文乱码问题
plt.rcParams["font.sans-serif"]="SimHei"
#解决负号无法正常显示的问题
plt.rcParams["axes.unicode_minus"] = False
df = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata.xlsx",sheet_name = "Sheet1",usecols=[13,14,15])
#df.head()
df.boxplot()
plt.show()
plt.savefig(r"D:\python\pythoncode\pandas\figures\boxplot.jpg")
输出结果
从上图可以知道用户应还款总金额、本金、利息的分位数、最值、均值情况汇总如下表所示:
由表可知,用户借款金额几乎都是1000元,然而大多数的用户应还总额字段,不管是均值还是分位数,都超过原本正常应还本息和,这说明用户逾期还款人数多,罚金多则说明用户逾期时间长,该产品的用户资质不好。
下面从各个维度来描述该人群特征。
数据分析
性别与年龄
from pyecharts import Pie,Bar,Page
import pandas as pd
df3 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "性别",usecols=[0,1])
df4 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "年龄",usecols=[2,3])
pie = Pie('男性是女性的3倍',''
,width=500,height=400
)
pie.use_theme("dark")
pie.add('人数百分比'
,df3["性别"]
,df3["占比1"]
,radius = [40,75]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
bar=Bar("35岁以下的用户占87%,产品客群年轻化!","",width=500,height=400)
bar.use_theme("dark")
bar.add('各个年龄层占比'
,df4["年龄"]
,df4["占比2"]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
page=Page()
page.add(pie)
page.add(bar)
page.render()
运行结果
区域
from pyecharts import Bar
import pandas as pd
df = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "区域",usecols=[0,1])
bar = Bar('各个省份订单占比分布','四川、广东、湖北位列top3',width=1000
,height=400)
bar.use_theme("dark")
bar.add('' #注解==label,图例
,df["省份"]#横坐标
,df["占比"]#纵坐标
,is_more_utils = True #设置最右侧工具栏
)
bar.render()
bar
运行结果:
订单状态
from pyecharts import Pie,Bar,Page
import pandas as pd
df3 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "订单状态",usecols=[0,1])
df4 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "逾期天数",usecols=[2,3])
bar1 = Bar('初审不通过比例超过72%',''
,width=500,height=400
)
bar1.use_theme("dark")
bar1.add('订单状态'
,df3["订单状态"]
,df3["占比1"]
,radius = [40,75]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
bar2=Bar("逾期超过30天占49%","",width=500,height=400)
bar2.use_theme("dark")
bar2.add('逾期天数'
,df4["逾期天数"]
,df4["占比2"]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
page=Page()
page.add(bar1)
page.add(bar2)
page.render()
运行结果
芝麻分分值分布
from pyecharts import Pie,Bar,Grid
import pandas as pd
df5 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "区域",usecols=[0,1])
bar=Bar("芝麻分700分以下超过97%!","",width=500,height=400)
bar.use_theme("dark")
bar.add('芝麻分各个分数段人数'
,df5["分组"]
,df5["数量"]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
bar.render()
运行结果
授信额度和借款期限
from pyecharts import Pie,Bar,Page
import pandas as pd
df3 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "授信额度",usecols=[0,1])
df4 = pd.read_excel(r"D:\python\pythoncode\pandas\borrowerdata2.xlsx",sheet_name = "借款期限",usecols=[2,3])
bar1 = Bar('99%的用户借款金额为1000元',''
,width=500,height=400
)
bar1.use_theme("dark")
bar1.add('授信额度'
,df3["额度"]
,df3["占比1"]
,radius = [40,75]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
bar2=Bar("96%d的用户选择还款周期为14天","",width=500,height=400)
bar2.use_theme("dark")
bar2.add('借款期限'
,df4["借款期限"]
,df4["占比2"]
,legend_pos ="right"
,is_label_show = True
,is_more_utils = True
)
page=Page()
page.add(bar1)
page.add(bar2)
page.render()
运行结果
结论
经过分析上图可以得出以下结论
1.用户基本信息
从性别上看,男性占75.89%,女性占24.11%,男性是主要借款人;
从年龄看,用户主要是95后占31%,90后占34%和85后占21%,年轻人是产品主力军;
从区域来看,用户数量排名前10的省份有四川、广东、湖北、江苏、山东、安徽、河南 、湖南 、河北 、浙江 ,排名前三城市分别为四川、广东和湖北省;
从时间上来看,第四季度下单人数最多,高达92.33%,其中11月和12月最多,分别为50.32%和38.93%。其次是1月份,2月份,然而其他月份极少,甚至为0。
2.订单状态
在三要素完全的情况下进行统计按订单的4种状态进行统计,发现初审不通过的订单占72%,按时还款用户占11%,有逾期表现的用户高达16%。结合该客群芝麻信用分分布情况,产品客群的还款能力和意愿极差。
3.借款
99%的用户会获得1000元的额度,日利率接近万五;借款天数分7天和14天两种,96%的用户选择14天之后还款,然而仅有6%的用户选择7天之后还款,他们更加愿意选择还款周期为14天。用户选择的是利率较高的短期小额借贷;
应用
通过爬取目标网页数据,找到逾期用户打上“坏用户”标签,充当Y标签,便于尽快诊断租房分期产品上用户的信用资质。这样不仅尽早管控降低风险,还避免了没有必要的损失。
附:爬虫code
#导入所需要的Python库,requests库用于请求网页获取网页数据,BeautifulSoup解析网页数据,time库的sleep()方法可以让程序暂停
from bs4 import BeautifulSoup
import requests
import time
import xlsxwriter
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
}
#判别房东性别(有待验证)
def judgment_sex(class_name):
if class_name == ['member_ico1']:
return '女'
else:
return '男'
#定义函数,用于获取进入详情页的链接
def get_links(url):
wb_data = requests.get(url,headers=headers)
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('#page_list > ul > li > a')
for link in links:
href = link.get("href")
get_info(href)
#定义函数,定位目标元素
def get_info(url):
wb_data = requests.get(url,headers=headers)
soup = BeautifulSoup(wb_data.text,'lxml')
id = soup.select('div.pho_info > h4')
addresses = soup.select('span.pr5')
capital = soup.select('#pricePart > div.day_l > span')
orderstatus = soup.select('#floatRightBox > div.js_box.clearfix > div.member_pic > a > img')
names = soup.select('#floatRightBox > div.js_box.clearfix > div.w_240 > h6 > a')
sexs = soup.select('#floatRightBox > div.js_box.clearfix > div.member_pic > div')
for tittle, address, price, img, name, sex in zip(tittles,addresses,prices,imgs,names,sexs):
data = [{
'id':tittle.get_text().strip(),
'address':address.get_text().strip(),
'capital':price.get_text(),
'orderstatus':img.get("src"),
'name':name.get_text(),
'sex':judgment_sex(sex.get("class"))
}]
print(data)
generate_excel(data)
#生成excel文件
def generate_excel(data):
workbook = xlsxwriter.Workbook('D:\\python\\pythoncode\\pyspider\\xiaozhuduanzu\\borrower_data.xlsx')
worksheet = workbook.add_worksheet()
#设定格式,等号左边格式名称自定义,字典中格式为指定选项
#bold:加粗,num_format:数字格式
bold_format = workbook.add_format({'bold':True})
money_format = workbook.add_format({'num_format':'$#,##0'})
date_format = workbook.add_format({'num_format':'mmmm d yyyy'})
#将两行两列设置宽度为15(从0开始)
worksheet.set_column(1,1,15)
#用符号标记位置,例如:A行1列
worksheet.write('A1', 'tittle', bold_format)
worksheet.write('B1', 'address', bold_format)
worksheet.write('C1', 'price', bold_format)
worksheet.write('D1', 'img', bold_format)
worksheet.write('E1', 'name', bold_format)
worksheet.write('F1', 'sex', bold_format)
row = 1
col = 0
for item in (data):
#使用write_string方法,指定数据格式写入数据
worksheet.write_string(row, col, str(item['tittle']))
worksheet.write_string(row, col + 1, str(item['address']))
worksheet.write_string(row, col + 2, str(item['price']))
worksheet.write_string(row, col + 3, str(item['img']))
worksheet.write_string(row, col + 4, str(item['name']))
worksheet.write_string(row, col + 5, str(item['sex']))
row += 1
workbook.close()
if __name__ == '__main__':
#制作翻页循环
urls = ['http://**.*******.com/orderstatus-p{}-0/'.format(number) for number in range(1,14)]
for single_url in urls:
get_links(single_url)
time.sleep(2)
generate_excel(data)