python语言,在数据分析方面,涵盖了“数据获取→数据处理→数据分析→数据可视化”这个流程中每个环节,不可不谓之为利器。
本篇文章我们就利用python语言在数据分析方面的应用,来实现从数据的抓取,清洗到分析和数据可视化,这个完整的闭环:
通过使用requests库对链家网二手房列表页进行抓取。
通过BeautifulSoup对页面进行解析,并从中获取房源价格,面积,户型和关注度的数据。
最后再利用python的pandas,matplotlib模块对数据进行处理和分析。
0,环境搭建
环境:win10+Anaconda +jupyter Notebook
模块:
爬虫和网页解析相关的模块:requests,BeautifulSoup,time
数据分析模块:Numpy,pandas,
画图模块:matplotlib,
1,构建爬虫,抓取需要的信息
A,导入爬虫相关的模块
import requests,time
from bs4 import BeautifulSoup
requests,负责网络请求。
BeautifulSoup,负责网页解析。
Time,负责设置每次抓取的休息时间。
B,分析网页
开始抓取前先观察下目标页面或网站的结构,其中比较重要的是URL的结构。
链家网的二手房列表页面共有100个,URL结构为http://bj.lianjia.com/ershoufang/pg2/,其中bj表示城市,/ershoufang/是频道名称,pg3是页面码。我们要抓取的是北京的二手房频道,所以前面的部分不会变,属于固定部分,后面的页面码需要在1-100间变化,属于可变部分。我们可以用for循环构造要请求的url。
#这里,我们抓取100个页面的数据
for i in range(1, 100):
url='http://bj.lianjia.com/ershoufang/pg'+str(i)+"/"
#我们可以先看下网址是否构造正确
for i in range(1, 10):
url='http://bj.lianjia.com/ershoufang/pg'+str(i)+"/"
print(url)
#输出:
https://bj.lianjia.com/ershoufang/pg1/
https://bj.lianjia.com/ershoufang/pg2/
https://bj.lianjia.com/ershoufang/pg3/
https://bj.lianjia.com/ershoufang/pg4/
https://bj.lianjia.com/ershoufang/pg5/
https://bj.lianjia.com/ershoufang/pg6/
https://bj.lianjia.com/ershoufang/pg7/
https://bj.lianjia.com/ershoufang/pg8/
https://bj.lianjia.com/ershoufang/pg9/
C,构造爬虫
headers={
'Accept':'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding':'gzip, deflate, br',
'Accept-Language':'zh-CN,zh;q=0.8',
'Connection':'keep-alive',
'Referer':'http://www.baidu.com/link?url=_andhfsjjjKRgEWkj7i9cFmYYGsisrnm2A-TN3XZDQXxvGsM9k9ZZSnikW2Yds4s&wd=&eqid=c3435a7d00006bd600000003582bfd1f'
}
for i in range(1, 100):
url='http://bj.lianjia.com/ershoufang/pg'+str(i)+"/"
res = requests.get(url=url,headers=headers)
html = res.text
time.sleep(0.7)
在这里需要特别注意一下:为了尽量伪装成正常的请求,我们需要在http请求中设置一个头部信息,否则很容易被封。头部信息网上有很多现成的。
手动设置每次请求的时间间隔。
D,输出到屏幕上,看看爬取是否成功
ok,没问题,我们接下来,对这些页面进行解析,提取需要的信息。
E,解析页面,提取信息
lj=BeautifulSoup(html,'html.parser')
#提取价格信息
price = lj.find_all("div",attrs={"class":"priceInfo"})
tpc = []
for p in price:
totalPrice = p.span.string
tpc.append(totalPrice)
#提取房源信息
houseInfo = lj.find_all("div",attrs={"class":"houseInfo"})
hio=[]
for h in houseInfo:
house = h.get_text()
hio.append(house)
#提取关注度信息
followInfo = lj.find_all("div",attrs={"class":"followInfo"})
fio = []
for f in followInfo:
follow = f.get_text()
fio.append(follow)
##创建数据表(构造DataFrame)
import pandas as pd
house = pd.DataFrame({"totalprice":tpc,"houseinfo":hio,"followinfo":fio})
#检查下数据集构造的情况
house.head()
虽然我们已经把提取的信息构造为DataFarme结构的数据,但明显能看到,这个数据集有些粗糙。如房源信息,在数据集中每个房源的小区名称,户型,面积,朝向等信息都在一个字段中,无法直接使用。所以我们还需要,对数据集进行进一步的处理。
2,数据处理(特征构造)
数据处理的内容主要是对数据集进行数据清洗和加工。
数据清洗:
当我们利用爬虫从网络中爬取数据,数据集中容易出现垃圾数据(空格,乱码,特殊符号等),空值,异常值(字母大小写未区分,不匹配的数据类型等)问题,对数据集做处理时候,首先要特别注意这些问题(这属于数据处理中的准备工作,这里里就不展示了,有兴趣的朋友可以看看专栏中之前的几篇文章,都有说明)。
数据加工:
数据清洗完毕后,我们根据分析所需要的数据信息,再对数据集的字段,进行一些新的特征的构造。
数据处理的的目的是为了让数据集变得更利于下一步分析。
从houseinfo字段中,新构造出“小区”,“户型”,“面积”等特征
从followinfo字段中,新构造出“关注度”特征
其实这里的操作,就相当于Excel中的分列操作,在分列操作中,尽量把原有字段切分成
每一个新字段都是最小颗粒度(不可再分)特征的字段。
house["xiaoqu"] = house["houseinfo"].apply(lambda x: x.split("|")[0].strip())
house["huxing"] = house["houseinfo"].apply(lambda x: x.split("|")[1].strip())
house["mianji"] = house["houseinfo"].apply(lambda x: x.split("|")[2].strip())
house["guanzhu"] = house["followinfo"].apply(lambda x: x.split("|")[0].strip())从面积字段中提取面积的数字,注意这里要转化为float格式,以便于后续的计算
#定义函数,利用正则表达式,取得mianji字段中,字符串中的数字
import re
def get_num(string):
return (re.findall("\d+\.?\d*",string)[0])
#使用apply()方法,对mianji使用函数get_num
house["mianji_num"] = house.mianji.apply(get_num)
house.head()
#更改mianji_num字段格式为float
house['mianji_num']=house['mianji_num'].astype(float)
从关注字段中提取关注的数字,注意这里要转化为float格式,以便于后续的计算
#定义函数,利用正则表达式,取得followinfo字段中,字符串中的数字
import re
def get_num(string):
return (re.findall("\d+\.?\d*",string)[0])
#使用apply()方法,对guanzhu字段使用函数get_num
house["guanzhu_num"] = house.guanzhu.apply(get_num)
house.head()
#更改房源关注度及总价字段的格式
house[['guanzhu_num','totalprice']]=house[['guanzhu_num','totalprice']].astype(float)
3,数据分析(数据探索和可视化)
探索性数据分析(Exploratory Data Analysis,简称EDA)目的是最大化对数据的直觉,完成这个事情的方法只能是结合统计学的图形以各种形式展现出来。通常涉及以下几种方法的组合:原始数据集中每个字段的单变量可视化和汇总统计
数据集中每个自变量与目标变量之间的关系的双变量可视化和汇总统计
多元可视化以了解数据中不同字段之间的交互作用
让观察值聚类成有区别的小组
我们这里对处理后的数据简单的进行几个特征的探索性分析。
A,房源户型分布情况
前面我们经过对房源信息的分列获取了房源的朝向,户型等信息,这里我们对房源的户型情况进行汇总,看看北京在售二手房的户型分布情况。
首先按房源的户型对房源数量进行汇总:
huxing = house.groupby(["huxing"])["huxing"].agg("count")
huxing
#输出:
huxing
1室0厅 53
1室1厅 257
1室2厅 15
2室0厅 5
2室1厅 1425
2室2厅 270
2室3厅 1
3室0厅 4
3室1厅 495
3室2厅 297
3室3厅 8
4室1厅 20
4室2厅 83
4室3厅 11
5室2厅 15
5室3厅 1
6室1厅 1
6室2厅 3
6室4厅 1
Name: huxing, dtype: int64
绘制条形图,从户型的角度看下房源的分布:
#绘制房源户型分布条形图
'''
plt.rcParams['figure.figsize'] = (12, 8)#用来设置图像显示大小
plt.rcParams['font.sans-serif'] = ['STXihei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
'''
plt.rc('figure', figsize=(12, 8))#plt.figure(figsize = (12,8))
plt.rc('font', family='STXihei', size=15)
a=np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])
plt.barh([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],huxing,color='#99CC01',alpha=0.8,align='center',edgecolor='white')
plt.ylabel('户型')
plt.xlabel('数量')
plt.xlim(0,1500)
plt.ylim(0,20)
plt.title('房源户型分布情况')
plt.legend(['数量'], loc='upper right')
plt.grid(color='#95a5a6',linestyle='--', linewidth=1,axis='y',alpha=0.4)
plt.yticks(a,('1室0厅','1室1厅','1室2厅','2室0厅','2室1厅','2室2厅','2室2厅','3室0厅','3室1厅','3室2厅','3室3厅','4室1厅','4室2厅','4室3厅','5室2厅','5室3厅','6室1厅','6室2厅','6室3厅','6室4厅'))
plt.show()
北京在售二手房中户型从1室0厅到6室4厅近19种分布广泛。在所有的户型中数量最多的是2室1厅,其次为3室1厅和3室2厅,以及2室2厅。较小的1室1厅数量也较多。
B,房源面积分布情况
首先查看所有北京在售二手房的面积范围
#查看所有房源面积的范围值
house['mianji_num'].min(),house['mianji_num'].max()
#输出:
(25.550000000000001, 367.19)
绘制条形图,从面积的角度看下房源的分布:
#对房源面积进行分组
bins = [0, 50, 100, 150, 200, 250, 300, 350]
group_mianji = ['小于50', '50-100', '100-150', '150-200','200-250','250-300','300-350']
house['group_mianji'] = pd.cut(house['mianji_num'], bins, labels=group_mianji)
#按房源面积分组对房源数量进行汇总
group_mianji=house.groupby('group_mianji')['group_mianji'].agg(len)
绘制房源面积分布图
plt.figure(figsize = (12,8))
plt.rc('font', size=15)
a=np.array([1,2,3,4,5,6,7])
plt.barh([1,2,3,4,5,6,7],group_mianji,color='#99CC01',alpha=0.8,align='center',edgecolor='white')
plt.ylabel('面积分组')
plt.xlabel('数量')
plt.title('房源面积分布')
plt.legend(['数量'], loc='upper right')
plt.grid(color='#95a5a6',linestyle='--', linewidth=1,axis='y',alpha=0.4)
plt.yticks(a,('小于50', '50-100', '100-150', '150-200','200-250','250-300','300-350'))
plt.show()
在所有房源中,数量最多的是50-100,其次为100-150。随着面积增加数量减少。小于50的小面积房源也有一定数量的房源。
C,房源关注度的分布情况
首先查看所有北京在售二手房的关注度区间
#查看房源关注度的区间
house['guanzhu_num'].min(),house['guanzhu_num'].max()
#输出:
(0.0, 965.0)
关注度从0到965。也就是说有些房子很热门,而有些房子没有人关注。
这可能和房源上线和更新的情况有关,此外还要考虑房源的销售速度,热门房源可能很抢手,刚上线就成交了。因此我们对情况进行简化,暂时忽略掉这些复杂的情况。仅对关注度的分布情况进行统计。
#对房源关注度进行分组
bins = [0, 100, 200, 300, 400, 500, 600, 700,800]
group_guanzhu = ['小于100', '100-200', '200-300', '300-400','400-500','500-600','600-700','700-800']
house['group_guanzhu'] = pd.cut(house['guanzhu_num'], bins, labels=group_guanzhu)
group_guanzhu=house.groupby('group_guanzhu')['group_guanzhu'].agg(len)
#绘制房源关注度分布图
plt.figure(figsize = (12,8))
plt.rc('font', size=15)
a=np.array([1,2,3,4,5,6,7,8])
plt.barh([1,2,3,4,5,6,7,8],group_guanzhu,color='#99CC01',alpha=0.8,align='center',edgecolor='white')
plt.ylabel('关注度分组')
plt.xlabel('数量')
plt.xlim(0,3000)
plt.title('房源关注度分布')
plt.legend(['数量'], loc='upper right')
plt.grid(color='#95a5a6',linestyle='--', linewidth=1,axis='y',alpha=0.4)
plt.yticks(a,('小于100', '100-200', '200-300', '300-400','400-500','500-600','600-700','700-800'))
plt.show()
在3000个房源中,近2500个房源的关注度小于100,关注度大于400的房源则较少。这里需要再次说明的是关注度数据无法准确的表示房源的热门程度。在实际业务中,热门房源可能由于出售速度快而关注度较少。
D,房源聚类分析
我们对所有在售房源按总价,面积和关注度进行聚类分析。将在售房源按总价,面积和关注度的相似性分在不同的类别中。
#导入sklearn中的KMeans进行聚类分析
from sklearn.cluster import KMeans
#使用房源总价,面积和关注度三个字段进行聚类
house_type = np.array(house[['totalprice','mianji_num','guanzhu_num']])
#设置n_clusters=3
clf=KMeans(n_clusters=3)
#计算聚类结果
clf=clf.fit(house_type)
#查看分类结果的中心坐标
clf.cluster_centers_
#输出:
array([[ 922.79075862, 119.53848276, 72.85241379],
[ 444.58505639, 81.17239192, 86.92575188],
[ 1866.09821429, 194.84982143, 78.375 ]])
#在原数据表中标注房子所属类别
house['label']= clf.labels_
根据三个类别在总价,面积和关注度三个点的中心坐标,我们将在售房源分为三个类别:
总价:低 面积 :低 关注度:高
总价:中 面积 :中 关注度:中
总价:高 面积 :高 关注度:低
第一个类别是总价低,面积低,关注度高的房源。
第二个类别是总价居中,面积居中,关注度居中的类别。
第三个类别是总价高,面积高,关注度低的类别。
从上述分析看出,按照实际业务出发,在广告和列表页的默认排序中应该给予总价400万左右,面积80平米左右的房源更高的权重。这个类别的房源可以吸引最多的用户关注。
结束语
数据分析的基本流程包括:数据获取→数据处理→数据分析→结果呈现。本篇文章我们利用python语言在数据分析方面的应用,实现了从数据的抓取,清洗到分析和数据可视化,整个完整的闭环。在实际操作中,往往并不像展示的这么简单,展示在文章中的只是分析流程,分析代码,以及分析结果。但这的背后的大量操作,往往十分琐碎苦逼。
但古人有句话说得好“亦余心之所善兮, 虽九死其犹未悔!”,借此话,以共勉!