Fork me on GitHub

深圳租房之旅

深圳租房之旅

最近,利用Python爬取了某个网站上关于深圳租房的一些信息,获得了2000*12的数据,然后利用pandas及第三方的库进行了数据清洗、分析和可视化的操作,对深圳的租房现状有了初步分析。

声明:数据仅用来学习,未用作任何商业用途

数据爬取

本次的数据是通过爬虫从网上获取的。很久没有爬数据了,把以前写的代码打开看了下,直接拿过来改了很多需要的信息,还是可以直接跑出结果。网站也没有反爬措施,获得数据蛮顺利的

导入各种库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np

import plotly as py
import plotly_express as px
from plotly.subplots import make_subplots # 画多个图
import plotly.graph_objects as go

import json
from lxml import etree
import requests
import xlwt
import re
import time

# 显示所有列
# pd.set_option('display.max_columns', None)

#显示所有行
# pd.set_option('display.max_rows', None)

#设置value的显示长度为100,默认为50
# pd.set_option('max_colwidth',100)

代码

代码中涉及到很多爬虫中需要用到的知识点:

  • 请求头的设置
  • xpath的使用
  • 将字典数据转成json格式,json包的使用
  • 数据保存到excel中:xlwt的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# 本案例仅供学习使用,未用作任何商业用途

class Leyoujia:

# 1. 初始化url和headers
def __init__(self):
self.start_url = 'https://shenzhen.leyoujia.com/zf/?n={}'
self.headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) "} # 需要换成实际的请求头

# 2. 得到全部的URL地址
def get_url_list(self):
url_list = [self.start_url.format(i) for i in range(101)] # 构造URL地址的列表形式并返回
return url_list

# 3. 发送请求,获取响应
def parse_url(self, url):
#print("parsing...", url)
response = requests.get(url=url, headers=self.headers)
return response.content.decode('utf-8', 'ignore') # 返回的是解析内容

# 4. 获取数据
def get_content_list(self, html_str):
html = etree.HTML(html_str)
div_list = html.xpath("/html/body/div[3]/div[2]/div[1]/div[5]/ul/li")

content_list = []
for div in div_list:
item = {"layout":"","location":"","size":"","sizeInside":"",
"zhuangxiu":"","numberFloor":"","time":"","name":"","zone":"",
"position":"","way":"","money":""}

item["layout"] = div.xpath(".//div[2]/p[2]/span[1]/text()")
item["location"] = div.xpath(".//div[2]/p[2]/span[2]/text()")
item["size"] = div.xpath(".//div[2]/p[2]/span[3]/text()")
item["sizeInside"] = div.xpath(".//div[2]/p[2]/span[4]/text()")

item["zhuangxiu"] = div.xpath(".//div[2]/p[3]/span[1]/text()")
item["numberFloor"] = div.xpath(".//div[2]/p[3]/span[2]/text()")
item["time"] = div.xpath(".//div[2]/p[3]/span[3]/text()")
item["name"] = div.xpath(".//div[2]/p[4]/span[1]/a/text()")
item["zone"] = div.xpath(".//div[2]/p[4]/span[2]/a[1]/text()")
item["position"] = div.xpath(".//div[2]/p[4]/span[2]/a[2]/text()")
item["money"] = div.xpath(".//div[3]/p[1]/span/text()")
item["way"] = div.xpath(".//div[3]/p[2]/text()")

content_list.append(item)
return content_list

# 5. 保存数据
def save_content_list(self, content_list): # content_list是个列表,列表中的元素是item,item是个字典
with open("leyoujia.txt", "a", encoding="utf-8") as f :
for content in content_list:
f.write(json.dumps(content))
f.write("\n")

# 6. 数据保存到Excel中,使用xlwt(用于写入Excel中)
def save_to_excel(self, content_list):
workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('leyoujia') # 设置表名
head = ["name","layout","location","size","sizeInside","zhuangxiu",
"numberFloor","time","zone","position","money","way"] # 设置表头
for h in range(len(head)):
sheet.write(0, h, head[h])

length=len(content_list)
for j in range(1,length+1):
sheet.write(j,0,content_list[j-1]["name"])
sheet.write(j,1,content_list[j-1]["layout"])
sheet.write(j,2,content_list[j-1]["location"])

sheet.write(j,3,content_list[j-1]["size"])
sheet.write(j,4,content_list[j-1]["sizeInside"])
sheet.write(j,5,content_list[j-1]["zhuangxiu"])
sheet.write(j,6,content_list[j-1]["numberFloor"])
sheet.write(j,7,content_list[j-1]["time"])
sheet.write(j,8,content_list[j-1]["zone"])
sheet.write(j,9,content_list[j-1]["position"])
sheet.write(j,10,content_list[j-1]["money"])
sheet.write(j,11,content_list[j-1]["way"])

workbook.save('./leyoujia.xls')

def main(self):
# 获得url_list
url_list = self.get_url_list()
content_lists = []
# 在url_list中进行请求的发送,内容的获取以及保存数据
for url in url_list:
html_str = self.parse_url(url)
content_list = self.get_content_list(html_str)
self.save_content_list(content_list) # 保存content_list
content_lists.extend(content_list) # 将所有的content_list全部追加到content_lists
self.save_to_excel(content_lists) # 保存到excel中

if __name__ == '__main__':
time.sleep(1)
leyoujia = Leyoujia()
leyoujia.main()

数据处理

读取数据

将上面保存的数据读取从本地读取出来

字段含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""
name: 小区的名字
laytou:户型
location:朝向
size:房子建筑面积大小
sizeInside:套内面积大小
zhuangxiu:精装、豪装、普装、毛坯
numberFloor:楼层数
time:建成时间
zone:区
position:所在区的具体位置
money:价格
way:出租方式(整租或者合租)
"""

原始数据信息

删除缺失值

使用的是dropna函数,两个重要的参数:

  • axis:0表示行,1表示列
  • how:any表示至少有一个缺失值,all表示必须全部为缺失值

字段处理

为何处理

对于数据中的几个字段,我们需要的只是其中的数字信息,所以需要将它们从整个文本中提取出来。

处理方法

根据表格中文本不同,介绍3种方法:

  • 通过apply函数
  • 通过正则表达式来进行匹配
  • 通过replace方法进行替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
df1 = df.copy()

# 方式1:通过自定义的函数,传给apply方法
def apply_size(x):
return float(x.split("面积")[1].split("㎡")[0])

def apply_sizeInside(x):
return float(x.split("面积")[1].split("㎡")[0])

def apply_way(x):
return x.split("|")[0]

def apply_room(x):
return x.split("室")[0]

df1["sizeInside"] = df1["sizeInside"].apply(apply_sizeInside)
df1["size"] = df1["size"].apply(apply_size)
df1["room"] = df1["layout"].apply(apply_room) # 增加一列数据:卧室个数,从layout中分割出来
df1["way"] = df1["way"].apply(apply_way)

# 方式2:获取文本中的数据,正则表达式
df1["numberFloor"] = df1["numberFloor"].map(lambda str:re.findall(r"\d+",str)[0]).astype(dtype="int") #

# 方式3:将不需要的内容替换成空格,str.replace
df1["time"] = df1["time"].str.replace("年建成","").astype(dtype="int")

df1.head()

处理前后对比

处理前

处理后:增加了room字段

同时处理后的字段类型也发生了变化:

单个特征可视化

租房方式-way

对租房方式进行可视化:从数据和图形可以直接看出来,绝大多数的人还是选择整租

区域-zone

想对比每个区的房源出租情况,从数据和图形中看出来:

  • 福田作为CBD,房源最多;其次是龙华和龙岗,2个老工业区
  • 南山作为科技中心,紧随其后
  • 坪山、光明、盐田3个区比较落后,房源少

装修方式-zhuangxiu

通过不同的装修方式来分析对比各种房源的数量。不同的参数来实现颜色的变化;

结论:房源最多的还是集中在精装普装方式上

房子朝向-location

比较房子的朝向来分析对房源数量的影响

前3名分别是:朝南、朝南北、朝北

居室个数-room

房子里面卧室的个数对租房的影响,分析不同数量的占比

区与房价的关系

在每个区的房租价格肯定是不同的,通过热力图来进行对比

结论:南山和福田的房价整体是偏高的

装修风格与房租价格关系

时间与房租价格

随着时间的不断变化,每个区域的房租价格也在跟着变化,通过散点图来观察每个区的价格分布

关内

通过观察关内的数据分布,可以看到:

  • 南山和福田的整体价格高于罗湖和盐田
  • 南山的均价几乎在20k左右
  • 盐田的整体价格非常低
  • 罗湖的价格比较平均,波动较小

image-20200703201131031

关外

  • 关外的价格整体偏低,均价在10k不到
  • 宝安和龙岗偶尔出现高价
  • 坪山房价偏低

多特征的可视化

在这里以南山区进行分析

作图数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 用于制作小提琴图

nanshan = df1[df1["zone"] == "南山"]
nanshan
# 用于制作柱状图

nanshan_position = nanshan["position"].value_counts().reset_index().rename(columns={"index":"position","position":"number"})
nanshan_position

# 用于饼图的制作

nanshan_room = nanshan["room"].value_counts().reset_index().rename(columns={"index":"room","room":"number"})
nanshan_room

# 用于散点图的制作
px.scatter(nanshan,x="numberFloor",y="money",color="position",color_continuous_scale='Inferno')

多特征-多图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
position_list = nanshan_position.position.tolist()

fig = make_subplots(rows=2, cols=2, # 1*2的子图
subplot_titles=("南山区房源分布","南山区租房价格分布"),
specs=[[{"type": "xy"}, {"type": "xy"}], # 每个子图的类型
[{"type": "domain"}, {"type": "xy"}]]
)
# 柱状图
fig.add_trace(go.Bar(x=position_list, # x=nanshan_position.position.tolist()
y=nanshan_position.number.tolist(),
text=nanshan_position.number.tolist(), # 文本显示在外面
textposition='outside'
),row=1,col=1)

# 小提琴图
for position in position_list:
fig.add_trace(go.Violin(x=nanshan['position'][nanshan['position'] == position],
y=nanshan['money'][nanshan['position'] == position],
name=position,box_visible=True,meanline_visible=True),
row=1, col=2
)
# 饼图
fig.add_trace(go.Pie(labels=nanshan_room.room.tolist(),
values=nanshan_room.number.tolist(),
textinfo='label+percent', # 将labels也显示出来
textposition="auto"), # 信息是否显示,显示在哪里?
row=2,col=1)

# 折线图
fig.add_trace(go.Scatter(x=nanshan.numberFloor.tolist(),
y=nanshan.money.tolist(),
mode='markers+text',
marker=dict(size=6,
color=nanshan.money.tolist(),
colorscale="haline"),
),
row=2,col=2)

# fig.update_traces(textposition="outside")
fig.update_layout(title_text="南山区租房情况", # 两个图的总标题(左上角)
height=1000,width=1000,
showlegend=False) # 隐藏右边的图例
fig.show()

其他区域的图形可以通过类似的方法

本文标题:深圳租房之旅

发布时间:2020年07月03日 - 20:07

原始链接:http://www.renpeter.cn/2020/07/03/%E6%B7%B1%E5%9C%B3%E7%A7%9F%E6%88%BF%E4%B9%8B%E6%97%85.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea