深圳租房之旅
2020-07-03 本文已影响0人
皮皮大
深圳租房之旅
最近,利用Python
爬取了某个网站上关于深圳租房的一些信息,获得了2000*12
的数据,然后利用pandas
及第三方的库进行了数据清洗、分析和可视化的操作,对深圳的租房现状有了初步分析。
![](https://img.haomeiwen.com/i5142014/59f1b956346bf1b1.jpg)
声明:数据仅用来学习,未用作任何商业用途
数据爬取
本次的数据是通过爬虫从网上获取的。很久没有爬数据了,把以前写的代码打开看了下,直接拿过来改了很多需要的信息,还是可以直接跑出结果。网站也没有反爬措施,获得数据蛮顺利的
导入各种库
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的使用
# 本案例仅供学习使用,未用作任何商业用途
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()
数据处理
读取数据
将上面保存的数据读取从本地读取出来
![](https://img.haomeiwen.com/i5142014/2293b63972667db4.jpg)
字段含义
"""
name: 小区的名字
laytou:户型
location:朝向
size:房子建筑面积大小
sizeInside:套内面积大小
zhuangxiu:精装、豪装、普装、毛坯
numberFloor:楼层数
time:建成时间
zone:区
position:所在区的具体位置
money:价格
way:出租方式(整租或者合租)
"""
原始数据信息
![](https://img.haomeiwen.com/i5142014/3463db4821b33654.jpg)
![](https://img.haomeiwen.com/i5142014/ec68ba57dea6e1cc.jpg)
删除缺失值
使用的是dropna函数,两个重要的参数:
- axis:0表示行,1表示列
- how:any表示至少有一个缺失值,all表示必须全部为缺失值
![](https://img.haomeiwen.com/i5142014/e128b619e74a8db5.jpg)
字段处理
为何处理
对于数据中的几个字段,我们需要的只是其中的数字信息,所以需要将它们从整个文本中提取出来。
处理方法
根据表格中文本不同,介绍3种方法:
- 通过
apply
函数 - 通过正则表达式来进行匹配
- 通过
replace
方法进行替换
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()
处理前后对比
处理前
![](https://img.haomeiwen.com/i5142014/6fd6192f780b549e.jpg)
处理后:增加了room字段
![](https://img.haomeiwen.com/i5142014/fb1a5891ddd6a247.jpg)
同时处理后的字段类型也发生了变化:
![](https://img.haomeiwen.com/i5142014/e582f851a98cb98b.jpg)
单个特征可视化
租房方式-way
对租房方式进行可视化:从数据和图形可以直接看出来,绝大多数的人还是选择整租
![](https://img.haomeiwen.com/i5142014/04b847fcf266f9f5.jpg)
![](https://img.haomeiwen.com/i5142014/a087e546be0dae30.jpg)
区域-zone
想对比每个区的房源出租情况,从数据和图形中看出来:
- 福田作为CBD,房源最多;其次是龙华和龙岗,2个老工业区
- 南山作为科技中心,紧随其后
- 坪山、光明、盐田3个区比较落后,房源少
![](https://img.haomeiwen.com/i5142014/f043a730a7546794.jpg)
![](https://img.haomeiwen.com/i5142014/cab0582dd9785289.jpg)
装修方式-zhuangxiu
通过不同的装修方式来分析对比各种房源的数量。不同的参数来实现颜色的变化;
![](https://img.haomeiwen.com/i5142014/7246916947b8794e.jpg)
![](https://img.haomeiwen.com/i5142014/8b9bd1587a6fa4cc.jpg)
![](https://img.haomeiwen.com/i5142014/a62003510bbe8477.jpg)
“
结论:房源最多的还是集中在精装
和普装
方式上
房子朝向-location
比较房子的朝向来分析对房源数量的影响。前3名分别是:朝南、朝南北、朝北
![](https://img.haomeiwen.com/i5142014/2b3211091b7a106f.jpg)
居室个数-room
房子里面卧室的个数对租房的影响,分析不同数量的占比
![](https://img.haomeiwen.com/i5142014/77717185ff551833.jpg)
区与房价的关系
在每个区的房租价格肯定是不同的,通过热力图来进行对比
结论:南山和福田的房价整体是偏高的
![](https://img.haomeiwen.com/i5142014/3c713ce4166ed2cd.jpg)
![](https://img.haomeiwen.com/i5142014/8a7fca4e2cf31327.jpg)
装修风格与房租价格关系
![](https://img.haomeiwen.com/i5142014/6f86ee1453bcc96a.jpg)
时间与房租价格
随着时间的不断变化,每个区域的房租价格也在跟着变化,通过散点图来观察每个区的价格分布
关内
通过观察关内的数据分布,可以看到:
- 南山和福田的整体价格高于罗湖和盐田
- 南山的均价几乎在20k左右
- 盐田的整体价格非常低
- 罗湖的价格比较平均,波动较小
![](https://img.haomeiwen.com/i5142014/122d3f2363543a31.jpg)
关外
- 关外的价格整体偏低,均价在10k不到
- 宝安和龙岗偶尔出现高价
- 坪山房价偏低
![](https://img.haomeiwen.com/i5142014/e27025c16813c725.jpg)
多特征的可视化
在这里以南山区进行分析
作图数据
# 用于制作小提琴图
nanshan = df1[df1["zone"] == "南山"]
# 用于制作柱状图
nanshan_position = nanshan["position"].value_counts().reset_index().rename(columns={"index":"position","position":"number"})
# 用于饼图的制作
nanshan_room = nanshan["room"].value_counts().reset_index().rename(columns={"index":"room","room":"number"})
# 用于散点图的制作
px.scatter(nanshan,x="numberFloor",y="money",color="position",color_continuous_scale='Inferno')
多特征-多图
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()
![](https://img.haomeiwen.com/i5142014/1b53f7cd0cceb5e3.jpg)
其他区的数据通过类似的方法得到相应的图形