深圳租房之旅
最近难菌,利用Python
爬取了某個網(wǎng)站上關(guān)于深圳租房的一些信息珠移,獲得了2000*12
的數(shù)據(jù)的畴,然后利用pandas
及第三方的庫進(jìn)行了數(shù)據(jù)清洗硫眨、分析和可視化的操作足淆,對深圳的租房現(xiàn)狀有了初步分析。
聲明:數(shù)據(jù)僅用來學(xué)習(xí),未用作任何商業(yè)用途
數(shù)據(jù)爬取
本次的數(shù)據(jù)是通過爬蟲從網(wǎng)上獲取的巧号。很久沒有爬數(shù)據(jù)了族奢,把以前寫的代碼打開看了下,直接拿過來改了很多需要的信息丹鸿,還是可以直接跑出結(jié)果越走。網(wǎng)站也沒有反爬措施,獲得數(shù)據(jù)蠻順利的
導(dǎo)入各種庫
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)
# 設(shè)置value的顯示長度為100靠欢,默認(rèn)為50
# pd.set_option('max_colwidth',100)`
代碼
代碼中涉及到很多爬蟲中需要用到的知識點(diǎn):
- 請求頭的設(shè)置
- xpath的使用
- 將字典數(shù)據(jù)轉(zhuǎn)成json格式廊敌,json包的使用
- 數(shù)據(jù)保存到excel中:xlwt的使用
# 本案例僅供學(xué)習(xí)使用,未用作任何商業(yè)用途
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) "} # 需要換成實(shí)際的請求頭
# 2. 得到全部的URL地址
def get_url_list(self):
url_list = [self.start_url.format(i) for i in range(101)] # 構(gòu)造URL地址的列表形式并返回
return url_list
# 3. 發(fā)送請求门怪,獲取響應(yīng)
def parse_url(self, url):
#print("parsing...", url)
response = requests.get(url=url, headers=self.headers)
return response.content.decode('utf-8', 'ignore') # 返回的是解析內(nèi)容
# 4. 獲取數(shù)據(jù)
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. 保存數(shù)據(jù)
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. 數(shù)據(jù)保存到Excel中掷空,使用xlwt(用于寫入Excel中)
def save_to_excel(self, content_list):
workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('leyoujia') # 設(shè)置表名
head = ["name","layout","location","size","sizeInside","zhuangxiu",
"numberFloor","time","zone","position","money","way"] # 設(shè)置表頭
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中進(jìn)行請求的發(fā)送肋殴,內(nèi)容的獲取以及保存數(shù)據(jù)
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()
數(shù)據(jù)處理
讀取數(shù)據(jù)
將上面保存的數(shù)據(jù)讀取從本地讀取出來
字段含義
"""
name: 小區(qū)的名字
laytou:戶型
location:朝向
size:房子建筑面積大小
sizeInside:套內(nèi)面積大小
zhuangxiu:精裝、豪裝拣帽、普裝疼电、毛坯
numberFloor:樓層數(shù)
time:建成時間
zone:區(qū)
position:所在區(qū)的具體位置
money:價(jià)格
way:出租方式(整租或者合租)
"""
原始數(shù)據(jù)信息
刪除缺失值
使用的是dropna函數(shù),兩個重要的參數(shù):
- axis:0表示行减拭,1表示列
- how:any表示至少有一個缺失值蔽豺,all表示必須全部為缺失值
字段處理
為何處理
對于數(shù)據(jù)中的幾個字段,我們需要的只是其中的數(shù)字信息拧粪,所以需要將它們從整個文本中提取出來修陡。
處理方法
根據(jù)表格中文本不同,介紹3種方法:
- 通過
apply
函數(shù) - 通過正則表達(dá)式來進(jìn)行匹配
- 通過
replace
方法進(jìn)行替換
df1 = df.copy()
# 方式1:通過自定義的函數(shù)可霎,傳給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) # 增加一列數(shù)據(jù):臥室個數(shù)魄鸦,從layout中分割出來
df1["way"] = df1["way"].apply(apply_way)
# 方式2:獲取文本中的數(shù)據(jù),正則表達(dá)式
df1["numberFloor"] = df1["numberFloor"].map(lambda str:re.findall(r"\d+",str)[0]).astype(dtype="int") #
# 方式3:將不需要的內(nèi)容替換成空格癣朗,str.replace
df1["time"] = df1["time"].str.replace("年建成","").astype(dtype="int")
df1.head()
處理前后對比
處理前
處理后:增加了room字段
同時處理后的字段類型也發(fā)生了變化:
單個特征可視化
租房方式-way
對租房方式進(jìn)行可視化:從數(shù)據(jù)和圖形可以直接看出來拾因,絕大多數(shù)的人還是選擇整租
區(qū)域-zone
想對比每個區(qū)的房源出租情況,從數(shù)據(jù)和圖形中看出來:
- 福田作為CBD旷余,房源最多绢记;其次是龍華和龍崗,2個老工業(yè)區(qū)
- 南山作為科技中心正卧,緊隨其后
- 坪山蠢熄、光明、鹽田3個區(qū)比較落后炉旷,房源少
裝修方式-zhuangxiu
通過不同的裝修方式來分析對比各種房源的數(shù)量签孔。不同的參數(shù)來實(shí)現(xiàn)顏色的變化叉讥;
“
結(jié)論:房源最多的還是集中在精裝
和普裝
方式上
房子朝向-location
比較房子的朝向來分析對房源數(shù)量的影響。前3名分別是:朝南饥追、朝南北图仓、朝北
居室個數(shù)-room
房子里面臥室的個數(shù)對租房的影響,分析不同數(shù)量的占比
區(qū)與房價(jià)的關(guān)系
在每個區(qū)的房租價(jià)格肯定是不同的判耕,通過熱力圖來進(jìn)行對比
結(jié)論:南山和福田的房價(jià)整體是偏高的
裝修風(fēng)格與房租價(jià)格關(guān)系
時間與房租價(jià)格
隨著時間的不斷變化透绩,每個區(qū)域的房租價(jià)格也在跟著變化,通過散點(diǎn)圖來觀察每個區(qū)的價(jià)格分布
關(guān)內(nèi)
通過觀察關(guān)內(nèi)的數(shù)據(jù)分布壁熄,可以看到:
- 南山和福田的整體價(jià)格高于羅湖和鹽田
- 南山的均價(jià)幾乎在20k左右
- 鹽田的整體價(jià)格非常低
- 羅湖的價(jià)格比較平均帚豪,波動較小
關(guān)外
- 關(guān)外的價(jià)格整體偏低,均價(jià)在10k不到
- 寶安和龍崗偶爾出現(xiàn)高價(jià)
- 坪山房價(jià)偏低
多特征的可視化
在這里以南山區(qū)進(jìn)行分析
作圖數(shù)據(jù)
# 用于制作小提琴圖
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"})
# 用于散點(diǎn)圖的制作
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=("南山區(qū)房源分布","南山區(qū)租房價(jià)格分布"),
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="南山區(qū)租房情況", # 兩個圖的總標(biāo)題(左上角)
height=1000,width=1000,
showlegend=False) # 隱藏右邊的圖例
fig.show()
其他區(qū)的數(shù)據(jù)通過類似的方法得到相應(yīng)的圖形