租房信息获取与数据分析

一.背景

        通过python爬虫获取lian-jia网上广州、深圳、佛山、东莞四个城市月租金在2500以内的房源信息,包括位置、价格、面积......  将获取到的房源信息进行数据分析,爬取的房源虽然包括公寓,但主要是对非公寓的房源进行数据分析。

二.数据获取

        数据爬取主要用到python的requests和靓汤(beautifulsoup)这两个库,靓汤主要是用于对html 或 xml 进行提取数据的。(ps:用re正则表达式 或 xpath 等其他的也行,用自己熟悉的方式提取到数据就行)爬取数据之前,先要了解url的结构,网页上自己所需要的内容,根据自己的需求编写对应的代码进行数据爬取。

1.URL分析

        批量的爬取数据,那就应该先分析不同页面url,首先看不同城市的url变化。

        从上图可以得知,gz和sz应该是两个城市的缩写,所以后面的爬取url在这个位置设置为变量值。

        接下来再看页数变化,url会有什么变化。

        要在pg后面的数字应该就是页数,erp2500就是限定的金额。同时在网站上面还能看到页面的数量最多展示到100页,所以我们在爬取的时候也要设定好爬取的页数。

2.房源列表信息爬取

        房源列表页面就有很多关键信息,所以我们先从中获取房源标题,区/(区域)、详细地址以及房源详情页的url。对应的请求获取数据的代码如下:

zf_url = []                                #用来存放url
zf_apartment = []                          #是/否公寓   1/0
zf_area = []                               #区
zf_site = []                               #详细地址
zf_title = []                              #标题
city = ['gz','sz','fs','dg']               #用来存放城市
city_name = ['广州','深圳','佛山','东莞']
for i in range(len(city)):
    city_url = f'https://{city[i]}.xxxxxx.com'   #爬取的网页url
    apartment_city = city_name[i]
    for j in range(1,101):
        html_url = city_url+ f'/zufang/pg{j}erp2500/'
        html_headers = {'User-Agent':'你的User-Agent',
                       'Connection': 'close'}

        response = requests.get(url=html_url,headers=html_headers)
        response.encoding='utf-8'
        soup = BeautifulSoup(response.text,'html.parser')
        
        #获取每个房源的url
        hdurls = soup.select('div.content__list--item a.content__list--item--aside')
        url_list = []       #用来存放此次url
        for hdurl in hdurls:
            url_list.append(city_url+ hdurl.attrs['href'])
            zf_url.append(city_url+ hdurl.attrs['href'])
    
        #获取标题
        for i in range(len(url_list)):
            title_list = soup.select('div.content__list--item--main p.content__list--item--title a')
            zf_title.append(title_list[i].text.replace('\n',' '))
        
        
        #判断是否公寓
        ls_list = []       #用来存放临时数据
        apartment_list = []
        for i in range(len(url_list)):
            ls_list.append(url_list[i].split('/')[3])
            if ls_list[i] == 'zufang':
                zf_apartment.append(0)
                apartment_list.append(0)
            elif ls_list[i] == 'apartment':
                zf_apartment.append(1)
                apartment_list.append(1)
        
        #获取每个房源的区、地址(公寓除外)
        areas = soup.select('p.content__list--item--des a')
        a = 0
        for i in range(len(apartment_list)):
            ls_num = 0
            if apartment_list[i] == 0:
                ls_num = a * 3
                zf_area.append(areas[ls_num].string) #区
                if areas[ls_num+1] is None :
                    areas[ls_num+1].string = ''
                if areas[ls_num+2] is None :
                    areas[ls_num+2].string = ''
                zf_site.append(areas[ls_num+1].string + areas[ls_num+2].string) #地址
                a = a + 1
            elif apartment_list[i] == 1:
                zf_area.append(apartment_city+'公寓')
                zf_site.append('/')

        zf_apartment 的1表示公寓,0是非公寓,设定这个值的主要原因是公寓详情页跟其他的不同,我能爬取到的内容较少(ps:看不懂提取方式的,去看一下beautifulsoup的语法,或者改用自己熟悉的提取方式)

3.房源详细信息爬取

        通过从房源列表爬取到的url对房源的详细信息进行爬取,包括租金、面积、类型、城市、楼层等信息。由于公寓的详情页面不一样,所以要判断是否公寓然后再进行对应的数据爬虫代码,具体代码如下:

for i in range(0,len(zf_url)):
    if zf_apartment[i] == 0:
        hd_response = requests.get(url=zf_url[i],headers=html_headers)
        hd_response.encoding='utf-8'
        hd_soup = BeautifulSoup(hd_response.text,'html.parser')

        #城市
        city = hd_soup.select('meta')[10]['content'].split('city=')[1]
        zf_city.append(city)
        
        #房子类型
        hd_detail = hd_soup.select('ul.content__aside__list li')
        if len(hd_detail) == 0:
            zf_type.append('/')
        else :
            hdtype = hd_detail[1].text.split(' ')[0].split(':')[1]
            zf_type.append(hdtype)
        
        
        hd_details = hd_soup.select('div#info li')
        if len(hd_details) == 0:
            #面积
            zf_proportion.append('0')
            #朝向
            zf_orientation.append('/')
            #楼层
            zf_height.append('/')
            #是否有电梯
            zf_elevator.append('无')
        else :
            #面积
            hdsize = hd_details[1].text.split(":")[1].split('㎡')[0]
            zf_proportion.append(hdsize)
            #朝向
            direction = hd_details[2].text.split(":")[1]
            zf_orientation.append(direction)
            #楼层
            height = hd_details[7].text.split(":")[1]
            zf_height.append(height)
            #是否有电梯
            elevator = hd_details[8].text.split(":")[1]
            zf_elevator.append(elevator)
        
        
        cash = hd_soup.select('div.table_content ul.table_row li')
        if len(cash) == 0:
            #月租金
            zf_price.append(0)
            #押金
            zf_book_price.append('/')
            #服务费
            zf_service_price.append('/')
        else :
            zf_price.append(cash[1].text)
            #押金
            zf_book_price.append(cash[2].text)
            #服务费
            zf_service_price.append(cash[3].text)
        
        
        
        
    elif zf_apartment[i] == 1:
        hd_response = requests.get(url=zf_url[i],headers=html_headers)
        hd_response.encoding='utf-8'
        apartment_soup = BeautifulSoup(hd_response.text,'html.parser')
        
        #城市
        city = apartment_soup.select('meta')[10]['content'].split('city=')[1]
        zf_city.append(city)
        #房子类型
        zf_type.append('公寓')
        #面积
        zf_proportion.append('0')
        #朝向
        zf_orientation.append('/')
        #公寓电梯
        ap_elevator = apartment_soup.select('div.flat__info--facilities ul li')
        if ap_elevator is None :
            zf_elevator.append('无')
        #print(len(ap_elevator))
        elif len(ap_elevator) < 2:
            zf_elevator.append('无')
        elif ap_elevator[1].text == '电梯':
            zf_elevator.append('有')
        else :
            zf_elevator.append('无')
            
        #楼层
        zf_height.append('/')
        
        #公寓地址
        ap_site = apartment_soup.select('div#info p')
        if len(ap_site) < 2:
            zf_site[i] = '/'
        else :
            zf_site[i] = re.sub(r'\d{11}','',ap_site[1].text.replace('\n',''))
        
        #公寓租金
        ap_cash = apartment_soup.select('div#aside p.content__aside--title span')
        if len(ap_cash) < 2:
            zf_price.append(0)
            #押金
            zf_book_price.append('/')
            #服务费
            zf_service_price.append('/')
        else:
            zf_price.append(ap_cash[1].text.replace('\n','').split('元')[0])
        
            #押金
            zf_book_price.append('/')
            #服务费
            zf_service_price.append('/')
        

        (ps:目前代码还没有进行优化,小伙伴们可以自己加一下多线程之类的提高爬虫效率。后面有时间会重新发一下优化后的代码,但是你看到这就说明我还没有优化☺☺)

三.数据预处理

   1.数据准备

zufang_data = pd.DataFrame()
zufang_data['city'] = zf_city
zufang_data['title'] = zf_title
zufang_data['area'] = zf_area
zufang_data['site'] = zf_site
zufang_data['type'] = zf_type
zufang_data['proportion'] = zf_proportion
zufang_data['height'] = zf_height
zufang_data['orientation'] = zf_orientation
zufang_data['elevator'] = zf_elevator
zufang_data['price'] = zf_price
zufang_data['book_price'] = zf_book_price
zufang_data['service_price'] = zf_service_price
zufang_data['url'] = zf_url
zufang_data

        网站上限制了100页,四座城市一共爬取到1.2W条房源数据。

2.检查数据

        可以看到数据是不存在空值的。

3.处理数据

        检查一下爬取数据的准确性,随便查看几个url对应的信息,在浏览器上输入url,上面的信息与网站上的信息进行对比验证准确性。例如:

        网站上可能会出现广告信息或者多次发布这种类型的房源,可以使用以下方法检查重复值或删除重复的房源信息:

zufang_data.duplicated()    #检验
zufang_data=zufang_data.drop_duplicates()   #去除重复值

        为了更好的观察一下整体的数据,可以先将数据导出excel,在excel里面看得更加清晰。

zufang_data.to_excel(r'zufang.xlsx',index=False)

        height字段表示的是楼层,个人觉得只要显示数字就行,所以对height这列的数据进行处理:

for i in range(len(zufang_data)):
    if '楼层/' in zufang_data['height'][i]:
        zufang_data['height'][i] = zufang_data['height'][i].split('层/')[1]
    if zufang_data['height'][i] != '/':
        zufang_data['height'][i] = zufang_data['height'][i].split('层')[0]
zufang_data['height']

        检验一下爬取的数据是否有虚假信息,通过面积与价格散点图进行观察,首先使用astype()方法将面积和价格的类型进行转换:

        然后绘制散点图:

import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']

zufang_data.plot.scatter(x='proportion', y='price', alpha=1)

        观察上图,有price超出了2500,这个有可能是爬取信息的时候,刚好这个房子租金涨了。再看有的面积竟然达到了2500㎡,太假了,连老实人都欺骗不了。按照实际情况取分析,2500在这四个城市,几乎不可能租到面积大于150㎡(保守的估计☺),所以要提取价格在2500以内,面积在150㎡以内的数据。

zufang_data = zufang_data[zufang_data['price'] <= 2500]
zufang_data = zufang_data[zufang_data['proportion'] < 150]
zufang_data

        面积(proportion)处于0的,都是公寓,因为在公寓的详情页面没有它的面积信息。

4.备份数据

        常见的是将数据备份到excel或者数据库中,备份到mysql数据库中的具体实现如下:

from datetime import datetime
from sqlalchemy import  create_engine,text
import pymysql

#  root:mysql用户名
#  123321:我的密码
#  pytest:数据库名,要先创建
#  name='表名'   
engine=create_engine('mysql+pymysql://root:123321@localhost/pytest?charset=utf8mb4')
zufang_data.to_sql(name='zufang_data',con=engine,index=False,if_exists='replace')

四.数据分析

1.词云图

        在查看房源时,一般我们设置好价格区间后,大多数都会先留意标题信息,这样我们能快速的了解到房子有什么特点。所以,我们可以提取这些标题的关键字,来制作词云图,从而了解到这些房源的主要特点。

首先是用jieba来提取关键字,我这里设置了提取50个关键字

import jieba
import jieba.analyse as anls
import pickle

title = " ".join(zufang_data['title']) 

#topK 用来设置关键词个数
keywords = anls.extract_tags(title, topK=50, withWeight=True) #返回含有两个值的字典  
keyword = []  #词
keyweight = [] #权重值
for word, weight in keywords:
    keyword.append(word)
    keyweight.append(weight)

        查看一下前10个词以及对应的权重值

        使用wordcloud生成词云图,我这里生成在本地的图片(还有可以直接生成的,可以自行百度)

# 将读取的文件进行切割
word = "".join(jieba.lcut(keyword))
wc = wordcloud.WordCloud(
    width=700,
    height=700,
    background_color="white",
    scale=15,
    font_path="msyh.ttc"
)
wc.generate(word)
wc.to_file("zufang.png")

        整租、独栋表达了房子的独立性、私密性,开间要表达的房子的面积空间大小。多种类型的房子提供给用户,包公寓、合租,单间等等,还会放一些关于朝向、采光、地铁这些租客想要了解的,用来吸引注意。

2.户型分析

        户型是租客在查看房源时很关心的点之一,以数量排在 前9的户型 以及 之后的作为其他类型的户型 制作饼图,这里使用了matplotlib库,具体如下:

import matplotlib.pyplot as plt

#分组获取户型数量
type_detail = zufang_data.groupby('type')['title'].count()
type_detail = type_detail.reset_index()
#type_detail

#排序 ,降序
types = type_detail.sort_values('title',ascending=False)
# 取前9个
type_detail = types[:9]

# 剩余的求和作为其他
type_order = types[10:]
type_order['title'] = type_order['title'].astype(numpy.int64)
type_order_count = type_order['title'].sum()
type_detail.loc[len(type_detail)] = ['其他',type_order_count]

# 创建饼图
plt.figure(figsize=(10, 10))  # 设置图像大小
plt.pie(type_detail['title'], labels=type_detail['type'], autopct='%1.1f%%', startangle=90)
plt.title('户型分布图')
 
# 显示图像
plt.show()

        整体前三的是1室1厅1卫占18.6%,公寓17.9%,3室2厅2卫占比16.4%。

        再看一下广州、深圳两个一线城市的户型分布(代码跟上面的差不多,只不过换成只有广深两个城市的数据)

        广深两城户型前三的分别是公寓31.8%,1室1厅1卫占14.1%,1室0厅1卫占比12%。

        结合两图可以看出,在广深两城,2500以内推荐的房源以公寓和单室为主,如果想选择适合一家人居住3室2厅2卫,在佛山和东莞会有更多的选择。

3.面积与区域分析

        对四座城市进行面积分析,月租金2500以内在四城一般以多少面积为主,在哪些区域,由于公寓面积没有爬取,都是0,所以就这里排除了对公寓的分析。

interval = 20  # 每个区间的宽度
bins = np.arange(1, 150 + interval, interval)  # 创建区间的边界
counts, edges = np.histogram(zufang_data[zufang_data['city'] == '广州']['proportion'], bins=bins)


# 使用matplotlib绘制直方图
plt.hist(zufang_data[zufang_data['city'] == '广州']['proportion'], bins=bins)

# 添加数值标签
for i, v in enumerate(counts):
    plt.text(edges[i], v, str(v), color='black')
 
 
# 设置x轴和y轴的标签
plt.title('gz房源面积图')
plt.xlabel('面积/㎡')
plt.ylabel('数量')
 
# 显示图表
plt.show()

        每个城市的分布各有不同,我们就去每座城市房源最多的两个区域进行分析,代码以广州的为例

gz_data = zufang_data[zufang_data['city'] == '广州']
gz_proportion1 = gz_data[gz_data['proportion'] > 80]
gz_proportion2 = gz_proportion1[gz_proportion1['proportion'] <=  100].groupby('area')['title'].count().reset_index()
gz_proportion2 = gz_proportion2.rename(columns={'title':'gz-1'})
gz_proportion2.sort_values('gz-1',ascending=False)

                                   

                         

        (xx-1表示面积区域最多数量,xx-2表示面积区域数量第二)

        ①.从上图看,在广州80-100㎡之间有523套,但是这些房子基本上距离市中心比较远,集中在增城、南沙、花都。在20-40㎡的有450套,这些房源各区都有,基本上是在市区或者交通很方便的城区。

        ②.深圳在20-40㎡的有723套,其中龙岗、宝安、罗湖位于前三位;0-20㎡的有603套,南山、福田、龙岗居前三,可以看出2500在0-40㎡在龙岗区的选择会比较多。

        ③.佛山房源80-100㎡的有823套,20-40㎡的有659套,房源主要集中在顺德、南海两区。

        ④.东莞房源80-100㎡的有824套,40-60㎡的有626套,东莞的房源分布比较广泛,上面只是展示了区域内前10的地区,总的来说2500在20-100㎡在东莞在全市都有不少的选择。

4.价格分析

        各市各区的每平方米的平均价格,代码如下:

#以广州为例,也是排除公寓
#分组获取各区的面积以及价格
gz_data = zufang_data[zufang_data['city'] == '广州']
gz_data = gz_data.groupby('area')['proportion','price'].sum().reset_index()

#求平均价格
gz_data['avg_price'] = 0   
for i in range(len(gz_data)):
    gz_data['avg_price'][i] = round(gz_data['price'][i]/gz_data['proportion'][i],2) 

#根据索引去除公寓
gz_data = gz_data.drop(4)

# 创建横向条形图
plt.barh(gz_data['area'], gz_data['avg_price'])

# 添加数值到每个条形旁边
for i, v in zip(gz_data['area'], gz_data['avg_price']):
    plt.text(v, i, v, ha='right', va='center')
 
plt.xlabel('price/㎡')
plt.ylabel('城区')
plt.title('gz各区1㎡价格')
plt.show()

        四个城市租金均价前十区域代码如下:

price_detail = zufang_data.groupby('area')['proportion','price'].sum().reset_index()

#求平均价格
price_detail['avg_price'] = 0   
for i in range(len(price_detail)):
    price_detail['avg_price'][i] = round(price_detail['price'][i]/price_detail['proportion'][i],2) 

#去除公寓
to_remove = price_detail['area'].str.contains('公寓')
price_detail = price_detail[~to_remove]
price_detail = price_detail.sort_values('avg_price',ascending=False)[:10]


# 创建横向条形图
plt.barh(price_detail['area'], price_detail['avg_price'],color = 'lightblue')

# 添加数值到每个条形旁边
for i, v in zip(price_detail['area'], price_detail['avg_price']):
    plt.text(v, i, v, ha='right', va='center')
 
plt.xlabel('price/㎡')
plt.ylabel('城区')
plt.title('四城租金均价最高前十区')
plt.show()

        四个城市全区域平均租金代码如下:

#去除公寓
to_remove = zufang_data['area'].str.contains('公寓')
price_detail = zufang_data[~to_remove]
price_detail = price_detail.groupby('city')['proportion','price'].sum().reset_index()

#求平均价格
price_detail['avg_price'] = 0   
for i in range(len(price_detail)):
    price_detail['avg_price'][i] = round(price_detail['price'][i]/price_detail['proportion'][i],2) 


price_detail = price_detail.sort_values('avg_price',ascending=False)

# 创建横向条形图
plt.barh(price_detail['city'], price_detail['avg_price'],color = 'lightgreen')

# 添加数值到每个条形旁边
for i, v in zip(price_detail['city'], price_detail['avg_price']):
    plt.text(v, i, v, ha='right', va='center')
 
plt.xlabel('price/㎡')
plt.ylabel('城区')
plt.title('四市全区域平均租金均价')
plt.show()

        2500以内的房源信息,四城整体都呈现出租金从市区到郊区逐渐下降,跟市场的整体趋势。广州的市中心区与郊区的租金差距较大,平均租金与同为一线城市的深圳比就显得租金便宜很多,与佛山、东莞相差不大;深圳的租金是均价最高的,四城均价前10里面有7个是深圳的;佛山时均价最低的城市,中心城区的价格也是四市最低的;东莞呈现出广泛,价格在各区域大体来看相对均匀。

        房源信息中包含有服务费这一项费用,这也是租客们关注点之一。下面 排除不需要服务费以及需要联系咨询服务费的,只看标明服务费用的房源在各区的占比情况。

#排除  需要联系的
to_remove = zufang_data['service_price'].str.contains('/')
service_price_detail = zufang_data[~to_remove]

# 排除 不需要服务费的
to_remove = service_price_detail['service_price'].str.contains("0")
service_price_detail = service_price_detail[~to_remove]

#根据区域 分组获取服务费的房源数量
service_count = service_price_detail.groupby('area')['service_price'].count().reset_index()
service_count = service_count.rename(columns={'service_price':'spd_total'})

#获取各区域的房源数量
count_data = zufang_data.groupby('area')['title'].count().reset_index()
count_data = count_data.rename(columns={'title':'total'})

#需要服务费区域的所有房源
service_count['total'] = 0
for i in range(len(service_count)):
    for j in range(len(count_data)):
        if service_count['area'][i] == count_data['area'][j] :
            service_count['total'][i] = count_data['total'][j]

#某区域需要服务费的占比
service_count['need_service_fee'] = 0
for i in range(len(service_count)):
    service_count['need_service_fee'][i] = round((service_count['spd_total'][i]/service_count['total'][i]),2)
service_count = service_count.sort_values('need_service_fee',ascending=False)

#转化为百分比
for i in range(len(service_count)):
    service_count['need_service_fee'][i] = str(service_count['need_service_fee'][i]) + '%'
service_count

        在标明需要服务费的房源信息来看,收取服务费的城市主要广深两城,并且推断出越是中心城区收取服务费的几率越大。

五.分析总结

        通过对租房信息的几方面分析,各市周边配套好的、交通方便的区域自然租金会比其他区域高,广州所呈现出来的有点像由外到内(郊区到市区)租金逐渐增长,中心城市与郊区的租金差距大;而深圳所呈现的就是整体的租金都比较高,这也是正常的,毕竟深圳发展迅速,机遇多、年轻人多,较高的工资也能承受较高的房租;佛山的平均租金是四个城市最低的,即便是靠近广州区域平均租金也不高,如果在广州非郊区区域工作,想要租金较低,周边配套好,交通方便,那么去佛山的中心区域租房未尝不是一个选择;东莞租房的房源分布就较为广泛,除了临近深圳的区域以及小部分主城区,整体的租金差距不是很大,选择会有很多。

# 1. 数据集说明 这是一份北京的租房数据,总计7000 多 条记录,分为 8 个同样结构的 CSV 数据文件。 # 2. 数据处理 首先通过 pandas 将这些数据文件合并到一起,然后进行数据处理,最后将清洗好的数据写到 sqlite 。 # 3.数据分析可视化 ## 3.1 整体情况 该数据集总共有 6024 个房源信息,平均每平米的租金为 169 元,每套房源的平均出租面积为 15.68 平米。 ## 3.2 地区分析 房源数量分布情况如下,可以看到朝阳和通州这两个地区的房源数量要远大于其它区,说明这两个地方的租赁市场比较活跃,人员流动和人口密度可能也比较大。 房租最贵的小区 TOP 10。半壁街南路 1 号院的房租最高,达到 596 元/平米,是平均值 169 元/平米的 **3** 倍。 从户型的房源数量分布来看,主要集中在 2-4 室的户型。之前也分析了,每套房源的平均出租面积为 15.68 平米,可见大部分房源都是合租,毕竟房租那么贵,生活成本太高了。 国家规定楼层 7 层以上需要装电梯,依据这个规定,我们根据楼层数来判断房源是否有电梯。 从下图可以看到,电梯房的房源数量比较多,毕竟楼层高,建的房子多,此外,电梯房平均每平米的租金也要比非电梯房贵 10 块钱。 在区分出电梯房之后,我们再引入楼层的纬度进行分析。 从租金上看,不管是电梯房还是非电梯房,低楼层的租金都会比较贵一些。因为北京地处北方,天气较干燥,不会有回南天,而且低楼层出行较为方便。电梯房的高楼层,租金也会比较贵,这大概是因为高楼层的风景较好。 > 南方天气潮湿,在春天的时候,有时会出现 回南天 这一气象,导致低楼层会出现地板、墙壁渗水,所以在南方一般都不爱租低层。 从房源数量上看,非电梯房的高层房源最多,低层房源最少。说明非电梯房的高层房源不容易租出去,这点在租金上也有所体现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值