查看原文
其他

Python爬取【昆明市】 二手房信息,做了一个可视化大屏,你是否买得起?

做时间的朋友🚀 快学Python 2023-05-04

一、项目背景

在改革开放后,我们国家对房地产业进行了市场化改革,各地房地产业发展速度非常快,过去十年已成为国民经济的重要支柱产业,显著地促进社会发展和经济提速,而房价一路攀升的同时,也对整个社会和我们的生活都带来较大的影响,在国家提出“房子是用来住的,不是用来炒的”背景下,政府已经实施大量措施,也使得房地产市场逐渐回归正轨,人们开始理性和冷静的思考房地产市场。虽然房地产热有所降温,但对我们每个普通人来讲,一套房子代表着一个安身立命之地,安居才能乐业。
可见,房子对于一个人的重要性。
当你每天穿梭在喧嚣的城市中,你想过你所在城市整体的房价情况怎么样吗?你也想知道哪些是富人区,也是你为之奋斗的目标吧?当你想买房并做好预算的时候,你是不是对购房所在的区域和地段无从选择?
现在,就让Python编程来教你做城市的房价分析,不仅对买房选择提供决策支持,还能在全局上让你对所在城市的房价有个总体性的认识,增加在社会交际中的“谈资”。另外,在技术方面,可以让初学的读者掌握Python第三方库Lxml的爬虫方法,以及优秀的可视化软件“Power BI”的使用方法。

二、城市房价信息可视化

此次实现的房价信息可视化效果图如下图所示(以我们国家美丽的云南春城昆明为例,对于其他城市的朋友,在文中会介绍如何改为自己所在的城市):

上图可以清晰的展示出房价分析的样本数量信息,分析样本的所在商圈,所在城市小区房价的排名情况,厅室结构对房价的影响,以及整个城市中所有商圈的房产价格排行情况。


从而可以很直观地从全局上了解到所在城市的房产价格大致信息,在结合着你对当地各个商圈实实在在的了解情况,可以说对整个城市的房价已经了然于胸。

三、程序实现

接下来就介绍如何从零开始去实现以上的功能,整个制作流程大致的思路是:获取所在城市的房价信息,对数据进行清洗加工,最后形成数据可视化的输出图表。
在数据获取方面,主要利用Python编程语言结合第三方库Lxml来完成,Lxml库是基于libxml2这一XML解析库的Python封装,该模块用C语言编写,解析速度相对较快,但该库仅适用于静态渲染数据的网页,AJAX异步加载数据的网站,则无法爬取。
在数据的可视化方面,比较常用的有Python编程结合Pyecharts、Matplotlib等第三方库编程实现,除此之外,还有比较优秀的数据分析软件Tableau和PowerBI,这两款软件在商业智能的数据分析和可视化方面都非常棒!都有简单、易用、快速的特点,在优势方面各有千秋。Tableau被称为计算机界的“梵高”,让枯燥的数据以优雅的图表形式展现出来,但该款软件试用期只有30天,如果购买正版的话是一笔不小的费用。而PowerBI则是微软公司开发的另一款非常优秀的数据分析软件,借助DAX(数据分析表达式)也成为数据分析的利器之一,上手比较容易,并且很多功能免费,所以我们可视化就选择PowerBI。
下面就按照实现的思路逐一进行详细介绍。

(一)数据获取及清洗加工

数据获取和清洗加工在数据的采集过程中就可以全部完成,数据获取的方式,采取Python网络爬虫的方式。Python语言是一门简单易学的编程语言,已经存在大量的第三方库可供直接调用,犹如站在一些技术大牛的“肩膀上”,可以很轻松地完成看似复杂的爬虫工作。  
在爬虫技术方面,常见的方法包括:静态网页数据的元素定位法,AJAX异步动态渲染加载获取json格式数据法,最后一个万能的方法是Selinum(一款Web自动化测试工具)模拟人对网页的操作获取数据。据我个人的经验,前两个方法是比较常用的,因为它们爬取数据的效率比Selinum高,而Selinum的最大的优势,则是一个万能的工具,几乎可以搞定所有的网站。所以在实际操作中,按照网页加载数据的形式,优先选择第一种和第二种,最后因为一些反爬虫技术实在搞不定,再采用Selinum吧。
关于发布房价信息的二手房网站,常见的有58同城和链家二手房信息,这两个网站我都试过,都是静态网页数据,很轻松就可以搞定,只是要看下哪个网站的数据更为丰富从而进行选择。据链家网介绍:“其楼盘字典收录了包括房源房间门牌号、标准户型图、属性信息、配套设施信息、历史业务数据等多维度信息。作为中国最大的楼盘数据库,链家网楼盘字典从房源录入的第一步便智能判别真实房源,确保房源100%真实性”。
我们做数据分析,前提条件是数据的真实性,如果数据不真实,则会产生误导,而很多商业网站因宣传需要,也可能存在一定的夸大成分,所以我们姑且信任他们的数据吧。
链家网二手房的网址为:
https://km.lianjia.com/ershoufang/
注意网址中的“km”代表城市昆明,所以打开该网站看到的全部房产信息是昆明市的,而如果我们把“km”改为“sh”后在谷歌浏览器中粘贴进去“https://sh.lianjia.com/ershoufang/”,则显示“上海”城市的二手房信息,同理,你可以按照你所在城市的名称规则进行修改,就可以定位到当地城市的二手房信息,实际上浏览器中是自动定位所在城市的,只是在后面的程序中需要进行城市名称修改。
当我们打开这个网页,浏览到网页的最下面,可以看到网页是100页,点击“下一页”按钮观察浏览器内网址的变化,如下图所示。

浏览器中的网址为:
https://km.lianjia.com/ershoufang/pg2
当我们尝试着把网址中“pg2”改为“pg3”后按下回车键,就自动跳转到第三页数据,以此类推可以发现,网页是以这个参数作为页面的控制,为后续的循环爬取100个网页创造了条件。
接下来,把光标放到关于每套房子的顶头详细描述的地方,点击右键弹出“检查”,就可以定位到该信息在网页结构的中的位置,如下图所示。

当定位到该元素后,例如上图的右侧红色方框部分,我们点击右键,弹出“Copy”菜单,接下来选择“Copy Xpath”

此时就可以得到:
//*[@id="content"]/div[1]/ul/li[1]/div[1]/div[1]/a

通过如下代码就可以爬取该字段的相应信息。
from lxml import etree
import requests

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}
html=requests.get('https://km.lianjia.com/ershoufang/',headers=headers)
selector=etree.HTML(html.text)
desc=selector.xpath('//*[@id="content"]/div[1]/ul/li[1]/div[1]/div[1]/a/text()')
print(desc)
结果如下:

同理,我们可以照葫芦画瓢,均可以很顺利地获取所有想要标签中的文字信息,所以就照猫画虎地对所有目标数据进行定位和获取,下面将实现爬取的核心代码进行详细讲解。
构造爬取每个页面的函数get_data(url),该函数的传递参数为url,即每个目标网页的网址。
def get_data(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}
    html=requests.get(url,headers=headers)
    selector=etree.HTML(html.text)
    tables=selector.xpath('//*[@id="content"]/div[1]/ul/li')
    for t in tables:
        if len(t) > 2:
            name = t.xpath('div[1]/div[1]/a/text()')[0] #
            Businessdistrict= t.xpath('div[1]/div[2]/div/a[1]/text()')[0] #所在商圈
            residentialQuarters  = t.xpath('div[1]/div[2]/div/a[2]/text()')[0] #所在小区
            describe = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[0] #厅室情况
            if describe.strip()!='车位':
                describe1 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[1].replace('平米','') #房屋面积
                describe2 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[2]  #房屋朝向
                describe3 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[3]  #装修情况
                describe4 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[4]  #楼层情况
                describe5 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[5]  #建筑情况
                all_value=float(t.xpath('div[1]/div[6]/div[1]/span/text()')[0].replace('万',''))*10000 #总价
                UnitPrice=t.xpath('div[1]/div[6]/div[2]/span/text()')[0].replace('单价','').replace('元/平','') #单价
            veryHouse_list=[name, Businessdistrict,residentialQuarters,describe,describe1,describe2,describe3,describe4,describe5,UnitPrice,all_value]
            house_list.append(veryHouse_list)
    print(url + '数据爬取完毕!')
    time.sleep(1)
其中:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}
为构造的请求头信息,目的是加入该请求头后以此来伪装成浏览器,以便更好的抓取数据,该请求头的获取,可以按下F5键刷新网页后找到User-Agent进行复制。
selector=etree.HTML(html.text)   
tables=selector.xpath('//*[@id="content"]/div[1]/ul/li')
上面两行代码,通过xpath路径获取每套房产的根节点信息。
for t in tables:
    if len(t) > 2:
        name = t.xpath('div[1]/div[1]/a/text()')[0] #
        Businessdistrict= t.xpath('div[1]/div[2]/div/a[1]/text()')[0] #所在商圈
        residentialQuarters  = t.xpath('div[1]/div[2]/div/a[2]/text()')[0] #所在小区
        describe = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[0] #厅室情况
        if describe.strip()!='车位':
            describe1 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[1].replace('平米','') #房屋面积
            describe2 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[2]  #房屋朝向
            describe3 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[3]  #装修情况
            describe4 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[4]  #楼层情况
            describe5 = t.xpath('div[1]/div[3]/div/text()')[0].split('|')[5]  #建筑情况
            all_value=float(t.xpath('div[1]/div[6]/div[1]/span/text()')[0].replace('万',''))*10000 #总价
            UnitPrice=t.xpath('div[1]/div[6]/div[2]/span/text()')[0].replace('单价','').replace('元/平','') #单价
        veryHouse_list=[name, Businessdistrict,residentialQuarters,describe,describe1,describe2,describe3,describe4,describe5,UnitPrice,all_value]
        house_list.append(veryHouse_list)
print(url + '数据爬取完毕!')
time.sleep(1)
上面的代码,实现的目的是在每个页面实现每套房产信息的获取。
例如:
name = t.xpath('div[1]/div[1]/a/text()')[0]
表示获取发布的房产的名称信息,关于xpath里面的路径定位,前面部分已经有过介绍,只要把鼠标放在要获取的文字网页上,点击右键,弹出“Copy”菜单,接下来选择“Copy Xpath”,既可以得到xpath路径,但是要获取中文文本信息,还需要在复制的路径后加上“/text()”,由于xpath获取的数据类型为列表(list)类型,因此需要按照索引把第0个元素取出来即可。剩余目标文字的爬取,均以此类推,只是在对数据清洗方面,使用到字符串类型数据的split()函数。该函数通过指定分隔符对字符串进行切片,返回结果的数据类型也是列表,也是按照索引将数据从列表中取出即可。
例如:
t.xpath('div[1]/div[3]/div/text()')
可以获取得到的数据样式为“['3室2厅 | 101.19平米 | 东 | 精装 | 中楼层(共34层)  | 塔楼']”,此时就需要先用索引0转换成字符串类型后,用split(‘|’)进行分割并返回列表类型的结果。
函数get_data(url)的最后一行代码是time.sleep(1),代表每当抓取一个页面,程序休息1秒钟,避免请求网页的频率太快而导致爬虫失败。
最后程序中的主函数如下:
if __name__ == "__main__":
    title = ['房屋描述', '所在小区', '所在商圈', '室厅', '面积(平方米)', '房屋朝向', '装修情况', '楼层情况', '建筑情况', '单价(元/平米)', '房产总价(万元)']
    urls=['https://km.lianjia.com/ershoufang/pg{}/'.format(str(i)) for i in range(1,101)]
    for url in urls:
        get_data(url)
    df = pd.DataFrame(house_list,columns=title)
    df.to_excel('C:\\爬取的数据.xlsx',index=False)
其中:
urls=['https://km.lianjia.com/ershoufang/pg{}/'.format(str(i)) for i in range(1,101)]
以上代码表示构造100个网址URL,构造以后依次循环调用get_data(url)函数,就可以依次获取每个页面的二手房信息,所有二手房信息都存储到house_list(数据类型为列表)中,最后构造一个数据类型为DataFrame的df,调用df.to_excel()函数即可将所有爬取的信息存储到Excel文件中,至此,通过爬虫获取当地城市的二手房信息已经完成,爬取下来的数据样式如下图所示。


如果对此感兴趣,想获取自己所在城市的房价,将URL中的km改为自己城市的拼音简称即可。

(二)数据可视化

当通过爬虫获取数据后,接下来就可以进行数据可视化了,打开PowerBI软件,界面如下图所示。

点击开始菜单下的“获取数据”,数据来源方式选择“Excel”,按照向导提示进行下一步操作,选择爬取下来的C盘位置“爬取的数据.xlsx”文件,加载数据后,就可以看到如下界面。
为了便于对数据可视化操作,我们先要建立两个度量值,PowerBI中的度量值是用DAX创建的一个虚拟的数据值,它不改变源数据,也不改变数据模型,如果不在图表上使用它,甚至不知道它是什么样子,一旦拖到图标上,它便发挥着巨大的作用,它可以随着切片器的筛选快速显示所需要的的动态结果,所以度量值一般在图表交互时使用。
点击上方的菜单“建模”,在上方空白栏处编写度量值,例如:新建名称为“房子数量”的度量值如下图所示。

房子数量= count(Sheet1[房屋描述]),另外,同样的方法新建度量值:平均单价 = AVERAGE(Sheet1[单价(元/平米)] ),怎么样?是不是特别简单,就如Excel公式里面的函数一样?
当上面两个度量值建好后,我们看到右边的字段里面多了“平均单价”和“房子数量”,但是这两个字段前面都有计算器的标识,说明是建立的度量值,如下图所示。

点解页面左上方的“报表”,即可进入可视化编辑页面,可视化操作页面如下图所示。
上图中空白部分为可视化编辑区域,可以将可视化的控件拖到该空白区域进行数据绑定和编辑,右侧依次是可视化的控件和字段,例如,找到可视化控件“卡片图”,拖到左边的区域,在可视化字段的配置界面,将最右侧的度量值“房子数量”拖到字段上,并且在格式中将标题文本改为“分析的样本数量”,即可获得如下的效果。

接着,在“卡片图”右侧拖入饼图,在右侧的可视化字段配置中,将最右侧的字段的“所在商圈”拖入到字段配置的图例下,并且将度量值“房子数量”拖到值下面,最后即可得到如下的效果图。

当我们把光标放到上面饼图时,它会自动显示该商圈内的房子数量,包括点击各个商圈,左边的“卡片图”中的数量也会随着联动变化,这就是度量值做动态图表的魅力!
接下来,在饼图的右侧依次拖入两个“簇状条形图”,在字段配置中轴分别为“所在小区”和“室厅”,而配置中的值均为度量值“平均单价”,在格式中的标题设置相应文本后,就可以看到如下的效果图。

再接下来,在可视化编辑区域的最下面,拖入一个“堆积柱形图”,在可视化字段配置中,将字段所在商圈拖到配置中的轴内,将度量值“平均房价”拖到配置中的值内,就可以达到如下效果了。
至此,整个数据可视化的工作就全部完成了,是不是很简单和快速高效,并且因为度量值的作用,整个图表示动态的。

通过整个可视化的房价信息表,采用了3000套房价信息作为分析样本,可以简单地得出以下总结:
  • 1.在我们祖国西南边陲春城昆明,房价最贵前三甲小区,当属彩云水榭、海东俊园和滇池卫城,而如果对当地有所了解的话,这几个小区,都靠近昆明滇池附近,是有名的富人区,所以房价最高也是理所当然。
  • 2.按照厅室结构来看,厅室很多的那种,一般属于别墅,别墅的单价,肯定不便宜吧?
  • 3.关于商圈,昆明城市均价最高的商圈依次是滇池半岛、度假区、小西门、翠湖等。滇池半岛和度假区靠近滇池富人区,而小西门和翠湖,则是昆明很有名的市中心哦,所以,数据还是可靠的哦,另外,我住在昆明站,可视化数据中的昆明站均价接近一万五,和实际情况差不多,因此,获取的数据还是具有一定的参考价值。

推荐阅读


新书上市


《算力:数字经济的新引擎》

互联网的普及,大数据、云计算、5G、人工智能、区块链等技术的成熟,促成了数字经济的大繁荣。以计算能力为基础,万物感知、万物互联、万物智能的数字经济新时代正在到来。数据量呈爆发式增长,对算力的需求达到空前高度,算力成为数字经济的新引擎。


↓ 点击阅读原文,查看作者新书立减69元!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存