深圳租房之旅

深圳租房之旅

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

image

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

数据爬取

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

导入各种库

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()

数据处理

读取数据

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

image

字段含义

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

原始数据信息

image
image

删除缺失值

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

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

字段处理

为何处理

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

处理方法

根据表格中文本不同,介绍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()

处理前后对比

处理前

image

处理后:增加了room字段

image

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

image

单个特征可视化

租房方式-way

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

image
image

区域-zone

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

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

装修方式-zhuangxiu

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

image
image
image


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

房子朝向-location

比较房子的朝向来分析对房源数量的影响。前3名分别是:朝南、朝南北、朝北

image

居室个数-room

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

image

区与房价的关系

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

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

image
image

装修风格与房租价格关系

image

时间与房租价格

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

关内

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

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

关外

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

多特征的可视化

在这里以南山区进行分析

作图数据


# 用于制作小提琴图
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()

image

其他区的数据通过类似的方法得到相应的图形

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355