|
|
51CTO旗下网站
|
|
移动端

用Python告诉你深圳房租有多高

最近各大一二线城市的房租都有上涨,究竟整体上涨到什么程度呢?我们也不得而知,于是乎 zone 为了一探究竟,便用 Python 爬取了房某下的深圳的租房数据,以下是本次的样本数据。

作者:zone7_来源:简书|2018-11-06 13:24

概述

  • 前言
  • 统计结果
  • 爬虫技术分析
  • 爬虫代码实现
  • 爬虫分析实现
  • 后记

前言

最近各大一二线城市的房租都有上涨,究竟整体上涨到什么程度呢?我们也不得而知,于是乎 zone 为了一探究竟,便用 Python 爬取了房某下的深圳的租房数据,以下是本次的样本数据:

用Python告诉你深圳房租有多高

样本数据

除去【不限】的数据(因为可能会与后面重叠),总数据量为 16971 ,其中后半部分地区数据量偏少,是由于该区房源确实不足。因此,此次调查也并非非常准确,权且当个娱乐项目,供大家观赏。

统计结果

我们且先看统计结果,然后再看技术分析。

深圳房源分布:(按区划分)

其中福田与南山的房源分布是最多的。但这两块地的房租可是不菲啊。

用Python告诉你深圳房租有多高

房源分布

房租单价:(每月每平方米单价 -- 平均数)

即是 1 平方米 1 个月的价格。方块越大,代表价格越高。

用Python告诉你深圳房租有多高

房租单价:平方米/月

可以看出福田与南山是独占鳌头,分别是 114.874 与 113.483 ,是其他地区的几倍。如果租个福田 20 平方的房间:

  • 114.874 x 20 = 2297.48

再来个两百的水电、物业:

  • 2297.48 + 200 = 2497.48

我们节俭一点来算的话,每天早餐 10 块,中午 25 块,晚饭 25 块:

  • 2497.48 + 50 x 30 = 3997.48

是的,仅仅是活下来就需要 3997.48 块。

隔断时间下个馆子,每个月买些衣服,交通费,谈个女朋友,与女朋友出去逛街,妥妥滴加个 3500

  • 3997.48 + 3500 = 7497.48

给爸妈一人一千:

  • 7497.48 + 2000 = 9497.48

月薪一万妥妥滴,变成了月光族。

房租单价:(每日每平方米单价 -- 平均数)

  • 即是 1 平方米 1 天的价格。 

用Python告诉你深圳房租有多高

租房单价:平方米/日

以前在乡下没有寸土寸金的感觉,那么可以到北上广深体验一下,福田区每平方米每天需要 3.829 元。[捂脸]

户型

户型主要以 3 室 2 厅与 2 室 2 厅为主。与小伙伴抱团租房是最好的选择了,不然与不认识的人一起合租,可能会发生一系列让你不舒服的事情。字体越大,代表户型数量越多。

用Python告诉你深圳房租有多高

户型

用Python告诉你深圳房租有多高

户型

租房面积统计

其中 30 - 90 平方米的租房占大多数,如今之计,也只能是几个小伙伴一起租房,抱团取暖了。

用Python告诉你深圳房租有多高

租房面积统计

租房描述词云

这是爬取的租房描述,其中字体越大,标识出现的次数越多。其中【精装修】占据了很大的部分,说明长租公寓也占领了很大一部分市场。

用Python告诉你深圳房租有多高

租房描述

爬虫思路

先爬取房某下深圳各个板块的数据,然后存进 MongoDB 数据库,最后再进行数据分析。

用Python告诉你深圳房租有多高

各个板块

数据库部分数据:

  1. /* 1 */  
  2.  
  3. "_id" : ObjectId("5b827d5e8a4c184e63fb1325"),  
  4. "traffic" : "距沙井电子城公交站约567米。",//交通描述  
  5. "address" : "宝安-沙井-名豪丽城",//地址  
  6. "price" : 3100,//价格  
  7. "area" : 110,//面积  
  8. "direction" : "朝南\r\n ",//朝向  
  9. "title" : "沙井 名豪丽城精装三房 家私齐拎包住 高层朝南随时看房",//标题  
  10. "rooms" : "3室2厅",//户型  
  11. "region" : "宝安"//地区 
  12.  

爬虫技术分析

  • 请求库:requests
  • HTML 解析:BeautifulSoup
  • 词云:wordcloud
  • 数据可视化:pyecharts
  • 数据库:MongoDB
  • 数据库连接:pymongo

爬虫代码实现

首先右键网页,查看页面源码,找出我们要爬取得部分。

用Python告诉你深圳房租有多高

源码

代码实现,由于篇幅原因只展示主要代码:(获取一个页面的数据)

  1. def getOnePageData(self, pageUrl, reginon="不限"):  
  2. rent = self.getCollection(self.region)  
  3. self.session.headers.update({  
  4. 'User-Agent''Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36'})  
  5. res = self.session.get(  
  6. pageUrl  
  7.  
  8. soup = BeautifulSoup(res.text, "html.parser" 
  9. divs = soup.find_all("dd", attrs={"class""info rel"}) # 获取需要爬取得 div  
  10. for div in divs:  
  11. ps = div.find_all("p" 
  12. try: # 捕获异常,因为页面中有些数据没有被填写完整,或者被插入了一条广告,则会没有相应的标签,所以会报错  
  13. for index, p in enumerate(ps): # 从源码中可以看出,每一条 p 标签都有我们想要的信息,故在此遍历 p 标签,  
  14. text = p.text.strip()  
  15. print(text) # 输出看看是否为我们想要的信息  
  16. print("===================================" 
  17. # 爬取并存进 MongoDB 数据库  
  18. roomMsg = ps[1].text.split("|" 
  19. # rentMsg 这样处理是因为有些信息未填写完整,导致对象报空  
  20. area = roomMsg[2].strip()[:len(roomMsg[2]) - 2]  
  21. rentMsg = self.getRentMsg(  
  22. ps[0].text.strip(), 
  23.  roomMsg[1].strip(), 
  24.  int(float(area)),  
  25. int(ps[len(ps) - 1].text.strip()[:len(ps[len(ps) - 1].text.strip()) - 3]),  
  26. ps[2].text.strip(),  
  27. ps[3].text.strip(),  
  28. ps[2].text.strip()[:2],  
  29. roomMsg[3],  
  30.  
  31. rent.insert(rentMsg)  
  32. except 
  33. continue 

数据分析实现

数据分析:

  1. # 求一个区的房租单价(平方米/元)  
  2. def getAvgPrice(self, region):  
  3. areaPinYin = self.getPinyin(region=region)  
  4. collection = self.zfdb[areaPinYin]  
  5. totalPrice = collection.aggregate([{'$group': {'_id''$region''total_price': {'$sum''$price'}}}])  
  6. totalArea = collection.aggregate([{'$group': {'_id''$region''total_area': {'$sum''$area'}}}])  
  7. totalPrice2 = list(totalPrice)[0]["total_price" 
  8. totalArea2 = list(totalArea)[0]["total_area" 
  9. return totalPrice2 / totalArea2  
  10. # 获取各个区 每个月一平方米需要多少钱  
  11. def getTotalAvgPrice(self):  
  12. totalAvgPriceList = []  
  13. totalAvgPriceDirList = []  
  14. for index, region in enumerate(self.getAreaList()):  
  15. avgPrice = self.getAvgPrice(region)  
  16. totalAvgPriceList.append(round(avgPrice, 3))  
  17. totalAvgPriceDirList.append({"value": round(avgPrice, 3), "name": region + " " + str(round(avgPrice, 3))})  
  18. return totalAvgPriceDirList  
  19. # 获取各个区 每一天一平方米需要多少钱  
  20. def getTotalAvgPricePerDay(self): 
  21.  totalAvgPriceList = []  
  22. for index, region in enumerate(self.getAreaList()):  
  23. avgPrice = self.getAvgPrice(region)  
  24. totalAvgPriceList.append(round(avgPrice / 30, 3))  
  25. return (self.getAreaList(), totalAvgPriceList)  
  26. # 获取各区统计样本数量  
  27. def getAnalycisNum(self):  
  28. analycisList = []  
  29. for index, region in enumerate(self.getAreaList()):  
  30. collection = self.zfdb[self.pinyinDir[region]]  
  31. print(region)  
  32. totalNum = collection.aggregate([{'$group': {'_id''''total_num': {'$sum': 1}}}])  
  33. totalNum2 = list(totalNum)[0]["total_num" 
  34. analycisList.append(totalNum2)  
  35. return (self.getAreaList(), analycisList)  
  36. # 获取各个区的房源比重  
  37. def getAreaWeight(self):  
  38. result = self.zfdb.rent.aggregate([{'$group': {'_id''$region''weight': {'$sum': 1}}}])  
  39. areaName = [] 
  40.  areaWeight = []  
  41. for item in result:  
  42. if item["_id"in self.getAreaList(): 
  43. areaWeight.append(item["weight"])  
  44. areaName.append(item["_id"])  
  45. print(item["_id"])  
  46. print(item["weight"])  
  47. # print(type(item))  
  48. return (areaName, areaWeight)  
  49. # 获取 title 数据,用于构建词云 
  50.  def getTitle(self):  
  51. collection = self.zfdb["rent" 
  52. queryArgs = {}  
  53. projectionFields = {'_id'False'title'True} # 用字典指定需要的字段  
  54. searchRes = collection.find(queryArgs, projection=projectionFields).limit(1000)  
  55. content = ''  
  56. for result in searchRes:  
  57. print(result["title"])  
  58. content += result["title" 
  59. return content  
  60. # 获取户型数据(例如:3 室 2 厅)  
  61. def getRooms(self): 
  62.  results = self.zfdb.rent.aggregate([{'$group': {'_id''$rooms''weight': {'$sum': 1}}}])  
  63. roomList = []  
  64. weightList = [] 
  65.  for result in results:  
  66. roomList.append(result["_id"])  
  67. weightList.append(result["weight"])  
  68. # print(list(result))  
  69. return (roomList, weightList)  
  70. # 获取租房面积  
  71. def getAcreage(self):  
  72. results0_30 = self.zfdb.rent.aggregate([ 
  73.  {'$match': {'area': {'$gt': 0, '$lte': 30}}},  
  74. {'$group': {'_id''''count': {'$sum': 1}}} 
  75.  ])  
  76. results30_60 = self.zfdb.rent.aggregate([  
  77. {'$match': {'area': {'$gt': 30, '$lte': 60}}},  
  78. {'$group': {'_id''''count': {'$sum': 1}}}  
  79. ])  
  80. results60_90 = self.zfdb.rent.aggregate([  
  81. {'$match': {'area': {'$gt': 60, '$lte': 90}}},  
  82. {'$group': {'_id''''count': {'$sum': 1}}}  
  83. ])  
  84. results90_120 = self.zfdb.rent.aggregate([  
  85. {'$match': {'area': {'$gt': 90, '$lte': 120}}}, 
  86.  {'$group': {'_id''''count': {'$sum': 1}}}  
  87. ]) 
  88.  results120_200 = self.zfdb.rent.aggregate([  
  89. {'$match': {'area': {'$gt': 120, '$lte': 200}}}, 
  90.  {'$group': {'_id''''count': {'$sum': 1}}}  
  91. ])  
  92. results200_300 = self.zfdb.rent.aggregate([  
  93. {'$match': {'area': {'$gt': 200, '$lte': 300}}},  
  94. {'$group': {'_id''''count': {'$sum': 1}}}  
  95. ])  
  96. results300_400 = self.zfdb.rent.aggregate([  
  97. {'$match': {'area': {'$gt': 300, '$lte': 400}}},  
  98. {'$group': {'_id''''count': {'$sum': 1}}}  
  99. ]) 
  100.  results400_10000 = self.zfdb.rent.aggregate([  
  101. {'$match': {'area': {'$gt': 300, '$lte': 10000}}},  
  102. {'$group': {'_id''''count': {'$sum': 1}}}  
  103. ])  
  104. results0_30_ = list(results0_30)[0]["count" 
  105. results30_60_ = list(results30_60)[0]["count" 
  106. results60_90_ = list(results60_90)[0]["count" 
  107. results90_120_ = list(results90_120)[0]["count" 
  108. results120_200_ = list(results120_200)[0]["count" 
  109. results200_300_ = list(results200_300)[0]["count" 
  110. results300_400_ = list(results300_400)[0]["count" 
  111. results400_10000_ = list(results400_10000)[0]["count" 
  112. attr = ["0-30平方米""30-60平方米""60-90平方米""90-120平方米""120-200平方米""200-300平方米""300-400平方米""400+平方米" 
  113. value = [  
  114. results0_30_, results30_60_, results60_90_, results90_120_, results120_200_, results200_300_, results300_400_, results400_10000_  
  115.  
  116. return (attr, value) 

数据展示:

  1. # 展示饼图  
  2. def showPie(self, title, attr, value):  
  3. from pyecharts import Pie  
  4. pie = Pie(title)  
  5. pie.add("aa", attr, value, is_label_show=True 
  6. pie.render()  
  7. # 展示矩形树图  
  8. def showTreeMap(self, title, data):  
  9. from pyecharts import TreeMap  
  10. data = data  
  11. treemap = TreeMap(title, width=1200, height=600)  
  12. treemap.add("深圳", data, is_label_show=True, label_pos='inside', label_text_size=19)  
  13. treemap.render()  
  14. # 展示条形图  
  15. def showLine(self, title, attr, value):  
  16. from pyecharts import Bar  
  17. bar = Bar(title)  
  18. bar.add("深圳", attr, value, is_convert=False, is_label_show=True, label_text_size=18, is_random=True 
  19. # xaxis_interval=0, xaxis_label_textsize=9,  
  20. legend_text_size=18, label_text_color=["#000"])  
  21. bar.render()  
  22. # 展示词云  
  23. def showWorkCloud(self, content, image_filename, font_filename, out_filename):  
  24. d = path.dirname(__name__)  
  25. # content = open(path.join(d, filename), 'rb').read()  
  26. # 基于TF-IDF算法的关键字抽取, topK返回频率最高的几项, 默认值为20, withWeight  
  27. # 为是否返回关键字的权重  
  28. tags = jieba.analyse.extract_tags(content, topK=100, withWeight=False 
  29. text = " ".join(tags)  
  30. # 需要显示的背景图片  
  31. img = imread(path.join(d, image_filename))  
  32. # 指定中文字体, 不然会乱码的  
  33. wc = WordCloud(font_path=font_filename,  
  34. background_color='black' 
  35. # 词云形状,  
  36. mask=img,  
  37. # 允许最大词汇  
  38. max_words=400,  
  39. # 最大号字体,如果不指定则为图像高度  
  40. max_font_size=100,  
  41. # 画布宽度和高度,如果设置了msak则不会生效  
  42. # width=600,  
  43. # height=400,  
  44. margin=2,  
  45. # 词语水平摆放的频率,默认为0.9.即竖直摆放的频率为0.1  
  46. prefer_horizontal=0.9  
  47.  
  48. wc.generate(text)  
  49. img_color = ImageColorGenerator(img)  
  50. plt.imshow(wc.recolor(color_func=img_color))  
  51. plt.axis("off" 
  52. plt.show()  
  53. wc.to_file(path.join(d, out_filename))  
  54. # 展示 pyecharts 的词云  
  55. def showPyechartsWordCloud(self, attr, value):  
  56. from pyecharts import WordCloud  
  57. wordcloud = WordCloud(width=1300, height=620)  
  58. wordcloud.add("", attr, value, word_size_range=[20, 100])  
  59. wordcloud.render() 

后记

最近还真是挺多事情发生的,房租的暴涨,其实是资本力量进驻了租房市场。自如、蛋壳这些长租公寓,相互抬高房租价格,而且让客户签第三方贷款协议,前期发展可能需要一点钱,但是到后期垄断市场之后,只要住房刚需在,就不会赚不回钱。最后,应对外界条件的变动,我们还是应该提升自己的硬实力,这样才能提升自己的生存能力。

【编辑推荐】

  1. 爬虫需谨慎!那些你不知道的爬虫反爬虫套路 学起来
  2. 从0到1:Python爬虫知识点梳理
  3. 不踩坑的Python爬虫:如何在一个月内学会爬取大规模数据
  4. 用爬虫分析互联网大数据行业薪资情况
  5. 《狄仁杰之四大天王》影评分析(爬虫+词云+热力图)
【责任编辑:未丽燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

视频课程+更多

讲师:刘宗泽3721人学习过

企业级Zabbix3.4+Grafana5.1全实战视频教程

企业级Zabbix3.4+Grafana5.1全实战视频教程

讲师:实践哥29040人学习过

 标杆徐2018最新Linux自动化运维系列④: Shell高级脚本自动化编程实战

标杆徐2018最新Linux自动化运维系列④: Shel

讲师:徐亮伟8250人学习过

读 书 +更多

网络管理员考前辅导

本书按照国家人事部、信息产业部全国计算机技术与软件专业资格(水平)考试要求编写,内容紧扣《网络管理员考试大纲》。全书共分为11章,覆...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊