项目背景
随着毕业季和暑假的到来,各大城市又要迎来一批小鲜肉了,摆在大家面前的第一件事就是关于住宿的事了。众所周知,北上广的房价对于大多数刚毕业的或者实习的同学们来说,即使拼爹也是比较难马上定居下来的,所以租房就成了大多数人必须的选择。本项目主要通过对链家网上海地区租房信息的爬取获得数据,进行数据分析后,给出合理的建议。
项目简介
本次项目主要是利用爬虫爬取链家网上关于租房方面的信息,然后通过数据清洗和加工,从而进行一些数据探索和分析,从而对整个上海租房市场现状有个了解,并且对在上海租房做出自己的建议。
数据来源和数据集
本项目所使用的数据集全部来自链家网,是使用 requests + BeautifulSoup 的方法对页面进行抓取和数据提取。通过使用 requests 库对链家网租房列表页进行抓取,通过 BeautifulSoup 对页面进行解析,并从中获取房源小区,面积,户型和价格等数据。
具体爬取过程见:Python爬取链家网上海市租房信息
本次爬取信息的时候,主要获得了以下信息:
内容字段小区名称 name、户型 room_type、面积 size、区县 region、街道地段 area、楼层 louceng、朝向 chaoxiang、月租价格 price、地铁线subway、到地铁线的距离distance、标题title等。
目的
主要是通过实际的数据分析对上海租房市场做一个简单的介绍,给有意向在上海租房的小伙伴们提供一些参考性的意见。具体来说,主要针对以下几个问题:
1、房源的地域性分布
2、户型的影响
3、楼层的影响
4、地铁及到地铁的距离的影响
5、不同的宣传标题是否会对销售有影响?有哪些关键词?
技术和工具
本项目主要分为两大部分:
第一部分是数据爬取,采用的是 requests + BeautifulSoup。
第二部分是数据分析,以 Python 编程语言为基础。数据分析部分主要使用 pandas 作为数据整理和统计分析的工具,matplotlib 用于图形的可视化,seaborn 库包用于图形美化。在进行宣传标题分析的时候,使用了jieba 作为分词工具包,并使用 wordcloud 包制作词云。
数据整理和清洗
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import seaborn as sns
import jieba
import jieba.analyse
from wordcloud import WordCloud
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']
#从csv文件中读取数据并存入DataFrame中
df = pd.read_csv('D:/jupyter/lianjia.csv',sep = ',',encoding='gbk') #必须得转码,否则后面显示乱码
#清理重复行
drop_df = df.drop_duplicates()
#初步观察数据情况
drop_df.info()
drop_df.describe()
drop_df.head(5)
经过数据的清洗和整理,我们可以看出,上海一共有39774套房源,平均租金为8142元/月,怎么会这么高,经过发现有个别58万/月的数据,同时楼层值为地上的房源都是别墅,数量少且租价高,需要对其再进行清洗。并且租房面积和租金有着密切的关系,改用每平方米月租金衡量(例如:总的平均月租金为8142.84/97.66=83.38元/平方米)。同时根据《上海市房屋租赁条例》,居住使用人的人均居住面积不得低于5平方米。针对以上几点对数据进行整理:
#补充缺失值
drop_df = drop_df.fillna({'distance':0,'subway':u'非地铁房'})
#清理异常值,选取租金和租房面积合适的房源
drop_df = drop_df[drop_df['louceng'] != u'地上'] #清理高价别墅房源
drop_df = drop_df[drop_df['price'] < 25000] #清理少数异常高价房源
drop_df = drop_df[drop_df['size'] > 5] #根据上海市要求,清理租房面积小于5平米的房源
#给表格添加每平方米月租金列
Price_average = (drop_df['price'] / drop_df['size']).round(2)
drop_df['Price_average'] = Price_average
drop_df.info()
drop_df.head(5)
房源的地域性分布情况
#观察地域分布情况
count_by_region = drop_df.groupby(['region'])['name'].count()
figure = plt.figure(figsize = (12,8))
ax1 = plt.subplot(111)
rect = ax1.bar(np.arange(len(count_by_region)),count_by_region.values,width =0.6)
#设置x轴刻度标签
def auto_xticks(rects,xticks):
x = []
for rect in rects:
x.append(rect.get_x() + rect.get_width()/2)
x = tuple(x)
plt.xticks(x,xticks)
auto_xticks(rect,count_by_region.index)
#设置数据标签数:
def auto_tag(rects, data = None, offset = [0,0]):
for rect in rects:
try:
height = rect.get_height()
plt.text(rect.get_x() + 0.1, 1.01 * height, '%s' % int(height))
except AttributeError:
x = range(len(data))
y = data.values
for i in range(len(x)):
plt.text(x[i] - 0.2 + offset[0],y[i] + .05 + offset[1],y[i])
auto_tag(rect)
plt.legend(rect,[u'房源数量'],loc = 'upper left')
#各区每平方月租均价
Price_per_square_meter = (drop_df.groupby(['region'])['Price_average'].mean()).round(2)
#绘制每平米月租金折线图
ax2 = ax1.twinx()
x = [i for i in range(len(Price_per_square_meter))]
y = Price_per_square_meter.values
rect2 = ax2.plot(x,y,'mo-', label = 'right')
auto_tag(rect2,Price_per_square_meter)
auto_xticks(rect,Price_per_square_meter.index)
ax1.set_title(u'不同区域的房源数量及每平米月租金')
plt.legend(rect2,[u'每平米月租金'],loc = 'upper right')
从上图可以看出四分之一以上的房源位于浦东,相对于其他地方浦东似乎更容易找到租住的房子,另外闵行,徐汇也有着较多的选择,但是根据每平米租金来算,徐汇,长宁的租金更贵,所以综合来看,浦东和闵行似乎更好一点,选择比较多,租金也是在可以接受的范围内。
#观察各区的月租金箱形图
count_by_region_rent = drop_df.groupby(['region'])['price']
mm =[]
for group in count_by_region.index:
v = count_by_region_rent.get_group(group).values
mm.append(v)
fig = plt.figure(figsize = (10,10))
ax3 = plt.subplot(111)
rect3 = ax3.boxplot(mm)
#设置标签
ax3.set_xticklabels(count_by_region.index)
plt.yticks(range(0,30000,2500))
从箱形图中可以看出各区的月租分布差距比较大,这和上海的一线城市的地位是相同的,不仅有新来的求职者,也有工作多年的白领和成功人士,从而导致了房租的分布差距大。经过对比上海市政规划可以看出,其中徐汇、长宁、静安等月租较高的原因可能是:
1、徐汇区有上海最多的各式花园洋房,决定了区域内租房市场多为高端租房。
2、上海最集中的涉外高标准住宅商务办公综合区、虹桥路沿线和西郊宾馆周围建有许多幢花园别墅,上海市大多数外国领事馆和外交官的官邸分布在长宁,果然是真正的土豪租房优选区域。
3、静安是上海出名的小资区域,其中有多处名人故居,文化气息浓郁;而且其中高档小区与写字楼都很多,导致了租金一直居高不下等。
户型分布情况
#查看户型分布情况
count_by_room_type = drop_df.groupby(['room_type'])['name'].count().sort_values(ascending = False)
figure = plt.figure(figsize=(15,9))
ax4 = plt.subplot(111)
rect4 = ax4.bar(np.arange(len(count_by_room_type)),count_by_room_type.values,width =0.4)
auto_tag(rect4)
auto_xticks(rect4,count_by_room_type.index)
ax4.set_title(u'户型分布情况')
plt.legend(rect4,[u'房源数量'],loc = 'upper right')
我们发现只有少数几种的户型数量比较多,其余的都非常少,明显属于长尾分布类型(严重偏态),所以,考虑将800套一下的户型统统归为一类。
# 把低于八百套的房型设置为其他
drop_df2 = drop_df.copy()
for i in range(len(drop_df2['room_type'])):
if drop_df2['room_type'].iloc[i] not in [u'2室2厅',u'2室1厅',u'3室2厅',u'1室1厅',u'4室2厅',u'3室1厅',u'1室0厅',u'1室2厅',u'2室0厅']:
drop_df2['room_type'].iloc[i] = u'其他'
new_room_type_count = drop_df2.groupby(['room_type'])['name'].count()
figure = plt.figure(figsize=(15,9))
ax5 = plt.subplot(111)
rect5 = ax5.bar(np.arange(len(new_room_type_count)),new_room_type_count.values,width =0.4)
auto_tag(rect5)
auto_xticks(rect5,new_room_type_count.index)
plt.legend(rect5,[u'房源数量'],loc = 'upper right')
#每个地区的单位面积月租均价
Price_per_by_room = (drop_df2.groupby(['room_type'])['Price_average'].mean()).round(2)
#绘制每平米月租金折线图
ax6 = ax5.twinx()
x = [i for i in range(len(Price_per_by_room))]
y = Price_per_by_room.values
rect6 = ax6.plot(x,y,'mo-', label = 'right')
auto_tag(rect6,Price_per_by_room)
plt.legend(rect6,[u'每平米月租金'],loc = 'upper center')
ax5.set_title(u'户型分布情况')
从上图可以看出2室和3室的房源数量最多,考虑到舒适性和生活的便利性,同时根据租金情况来看,不妨选择2室1厅,2室2厅合租的房源性价比更高。
楼层的影响
#查看楼层分布情况
count_by_louceng = drop_df.groupby(['louceng'])['name'].count().sort_values(ascending = False)
figure = plt.figure(figsize = (5,5))
ax7 = plt.subplot(111)
rect7 = ax7.bar(np.arange(len(count_by_louceng)),count_by_louceng.values,width =0.4)
auto_tag(rect7)
auto_xticks(rect7,count_by_louceng.index)
ax7.set_title(u'楼层数量分布情况')
#观察各区的月租金箱形图
count_by_louceng_rent = drop_df.groupby(['louceng'])['price']
nn =[]
for group in count_by_louceng.index:
v = count_by_louceng_rent.get_group(group).values
nn.append(v)
fig = plt.figure(figsize = (5,5))
ax9 = plt.subplot(111)
rect9 = ax9.boxplot(nn,whis = 1)
#设置标签
ax9.set_xticklabels(count_by_louceng.index)
plt.yticks(range(0,20000,3000))
由上图可以看出低中高楼层对房屋租金的影响不是特别大,但是高层的房屋价格相对于低层的房子要稍微便宜一点,可能与通勤时间有关。如果不在意每天爬楼的通勤时间,选择高区或者是个不错的选择。
地铁及到地铁的距离的影响
上海地铁是一线城市中最发达的,所以如果能够在地铁附近租房对日常的出行都有很大的便利,所以地铁房的价格也必然增加。
#查看地铁分布情况
count_by_subway = drop_df.groupby(['subway'])['name'].count().sort_values(ascending = False)
Price_average_of_subway = (drop_df.groupby(['subway'])['Price_average'].mean()).round(2)
angles = np.linspace(0, 2 * np.pi, len(Price_average_of_subway), endpoint=False)
data = np.concatenate((Price_average_of_subway.values, [Price_average_of_subway.values[0]])) # 闭合
angles = np.concatenate((angles, [angles[0]])) # 闭合
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111, polar=True)# polar参数!!
ax.plot(angles, data, 'bo-', linewidth=2)# 画线
ax.set_thetagrids(angles * 180/np.pi, Price_average_of_subway.index)
ax.set_title(u'上海各地铁线附近房源租金雷达图')
ax.set_rlim(0,130)
ax.grid(True)
for i in range(len(Price_average_of_subway)):
plt.text(angles[i]+0.01,data[i]+5,data[i])
plt.show()
4号线、10号线、13号线的房源价格最高,相对而言,非地铁房的价格较低,绝大多数都是低价房和少部分的高档别墅,这与我们的认知预测相符。我们单独的取房源最多的各区的地铁分布情况拿出来看:
j=0
for i in count_by_region.sort_values(ascending =False)[:4].index:
j += 1
#查看各区地铁分布情况
count_by_subway1 = drop_df[drop_df['region']== i].groupby(['subway'])['name'].count().sort_values(ascending = False)
Price_average_of_subway1 = (drop_df[drop_df['region']== i].groupby(['subway'])['Price_average'].mean()).round(2)
angles = np.linspace(0, 2 * np.pi, len(Price_average_of_subway1), endpoint=False)
data = np.concatenate((Price_average_of_subway1.values, [Price_average_of_subway1.values[0]])) # 闭合
angles = np.concatenate((angles, [angles[0]])) # 闭合
fig = plt.figure(figsize=(20,20))
ax = fig.add_subplot(2,4,j, polar=True)# polar参数!!
ax.plot(angles, data, 'bo-', linewidth=2)# 画线
ax.set_thetagrids(angles * 180/np.pi, Price_average_of_subway1.index)
ax.set_title(i+u'各地铁线附近房源租金雷达图')
ax.set_rlim(0,150)
ax.grid(True)
for i in range(len(Price_average_of_subway1)):
plt.text(angles[i]+0.01,data[i]+5,data[i])
由各区地铁雷达图可以看出不同区的地铁线路不同,价格也不一样,但是非地铁房的价格依然相对于地铁房的价格要便宜不少,如果公司离住的地方很近,没有地铁需求的话,可以找非地铁房比较划算。
#以浦东新区6号线为例,查看到地铁站距离对租金的影响
data_of_distance = drop_df[drop_df['region'] == u'浦东']
data_of_distance = data_of_distance[data_of_distance['subway'] == u'6号线']
dd = data_of_distance.sort_values(by = ['distance'])
#画散点图
fig = plt.figure(figsize = (10,9))
ax1 = fig.add_subplot(111)
ax1.scatter(dd['distance'].values,dd['Price_average'].values)
plt.xlabel(u'到地铁站距离')
plt.ylabel(u'租金')
ax1.set_title(u'地铁站距离对租金影响图')
由上图可以看出到地铁站的距离多在350m-750m之间,且在此距离内租金也相对较高一点。
不同的宣传标题关键词的影响
#处理title字段
#抽取关键词
def key_words(text):
key_words = jieba.analyse.extract_tags(text,topK = 20,withWeight = False,allowPOS=())
return key_words
drop_df['key_words'] = drop_df['title'].apply(key_words)
#创建一个文本,将关键词列表全部写入该文本
def new_text(wordlist):
f =open('D:/jupyter/wordlist_text.txt','a')
for word in wordlist:
f.writelines((word + u',').encode('utf-8'))
f.close()
drop_df['key_words'].apply(new_text)
text = open('wordlist_text.txt','r').read()
text = unicode(text,encoding = 'utf-8') #必须将utf-8转码为unicode中间值才能生成词云
my_wordcloud = WordCloud(width = 800,height = 400,background_color= 'white').generate(text) #读入文本写入词云
fig = plt.figure(figsize =(10,8))
plt.imshow(my_wordcloud)
plt.axis('off')
plt.show()
从词云上可以看出,宣传标题很大程度上反映了租客们的需求和关注点,地铁沿线,交通便利,生活方便,光线充足,视野开阔等是大家比较关注的一些租房要素,同时改善,精致等也有不小的比例,这类房源的客户对象应该是工作几年后有了积蓄后的改善性租房。同时一链倾城的出现频率接近最高,应该是与链家网的“一链倾城,有爱有家”的宣传口号有关系。
分析结论
通过上面的分析,我们可以得到的结论有这些:
1、租房的房源主要分布在浦东新区和闵行、宝山等郊区,租金也相对城区便宜很多;
2、徐汇、长宁、静安等中心城区的租房价格仍高居不下;
3、户型多为2室1厅,2室2厅,3室两厅,比较适合居家常驻,可以选择2室,3室的合租相对更加便利些;
4、低中高楼层对房屋租金的影响不是特别大,但是高层的房屋价格相对于低层的房子要稍微便宜一点,所以如果不在意每天爬楼的通勤时间,选择高区或许是个不错的选择;
5、不同城区的地铁线路不同,租金价格也不一样,非地铁房的价格要更加便宜;
6、地铁房到地铁站的距离多为350m—750m之间,租金也相对高一点;
7、选择租房的时候不妨多实地看看,地铁沿线,交通便利,生活方便,光线充足,视野开阔都是很好的衡量点。
总而言之,大家可以根据公司的地点选择合适的区域进行租房,能租在公司附近是最优的选择,如果无法在公司附近租房且手头较紧,浦东和闵行等区或许是个不错的选择,同时地铁比较发达,交通也比较方便。希望大家都能租到自己合适的房子。