pandas+groupby对南京二手房进行数据可视化及大图显示

 一、利用python的requests 库爬取链家二手房数据

链家南京二手房url地址:'https://nj.lianjia.com/ershoufang/'

按区查的话,比如鼓楼区,那么url就是'https://nj.lianjia.com/ershoufang/gulou/'

查看发现南京11个区里面,高淳房源太少,所以本次爬虫没有爬取高淳区的。

观察链家网站的页面发现最多显示100页的内容,所以本次爬取数据每个区爬取了100页,3000条数左右进行分析及可视化。

调用url获取到的数据如下:

和页面展示的房源信息一致。

页面上的信息复制粘贴下来会是对人性的压抑,但是我们url既然能调用出结果,那就可以提取出来。

页面展现出来的内容是用前端语言写的,我们用parsel库对网页进行解析,解析出re、xpath、css内容,然后根据css的内容进行匹配。

比如位置信息的元素是positionInfo,那么房子位置就可以根据该元素提取出来。 

houseInfo获取到的户型、面积、楼层等等信息是一整块的,为了方便后期数据处理,进行了正则匹配把户型、面积、建成年份等等提取出来。

整体代码如下:

import requests,re
import time,datetime
import pandas as pd
from bs4 import BeautifulSoup
import csv,parsel
import random

url= 'https://nj.lianjia.com/ershoufang/'
citys = ['gulou','jianye','qinhuai','xuanwu','yuhuatai','qixia','jiangning','pukou',
         'liuhe','lishui']    # 拼接url用
citys_ch = ['鼓楼','河西','秦淮','玄武','雨花台','栖霞','江宁','浦口','六合','溧水']   #保存各区数据用
items = [
    {'http': 'http://171.35.171.247:9999'},
    {'http': 'http://114.99.7.122:8752'}
]

for i in range(len(citys)):
    city = citys[i]
    f = open(str(citys_ch[i])+'二手房信息.csv', mode='a', encoding='utf-8_sig', newline='')
    csv_writer = csv.DictWriter(f, fieldnames=['标题', '房子位置', '房子信息', '户型','面积','装修','总共楼层','建成年份','房子结构','发布周期', '售价/万', '单价'])
    csv_writer.writeheader()
    for page in range(1, 101):
        print('===========================正在下载第{}页数据================================'.format(page))
        time.sleep(1)
        url = 'https://nj.lianjia.com/ershoufang/%s/pg{}/'.format(page) % city
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
        }
        proxies = random.choice(items)
        response = requests.get(url=url, headers=headers, proxies=proxies)
        selector = parsel.Selector(response.content.decode('utf-8'))
        lis = selector.css('.sellListContent li')
        dit = {}
        for li in lis:
            title = li.css('.title a::text').get()
            dit['标题'] = title
            positionInfo = li.css('.positionInfo a::text').getall()
            info = '-'.join(positionInfo)
            dit['房子位置'] = info
            houseInfo = li.css('.houseInfo::text').get()
            dit['房子信息'] = houseInfo

            partment = re.findall(r'^(\S+室\S+)\s+\|\s+', houseInfo)
            if partment ==[]:
                dit['户型'] = ""
            else:
                dit['户型'] = partment[0]
            area = re.findall(r'(\d+\S+平米)\s+\|\s+',houseInfo)
            if area ==[]:
                dit['面积'] = ""
            else:
                dit['面积'] = area[0]
            trim = re.findall(r'\s+(精装|简装|其他|毛坯)\s+\|\s+',houseInfo)
            if trim ==[]:
                dit['装修'] = ""
            else:
                dit['装修'] = trim[0]
            story = re.findall(r'\s+(\S+层\S+)\s+',houseInfo)
            if story ==[]:
                dit['总共楼层'] = ""
            else:
                dit['总共楼层'] = story[0]
            particular = re.findall(r'\s+(\d+)年建\s+',houseInfo)
            if particular ==[]:
                dit['建成年份'] = ""
            else:
                dit['建成年份'] = particular[0]
            struct = re.findall(r'\|\s+(板.*|塔.*)$',houseInfo)
            if struct!=[]:
                dit['房子结构'] = struct[0]
            else:
                dit['房子结构'] = ""

            followInfo = li.css('.followInfo::text').get()
            dit['发布周期'] = followInfo
            Price = li.css('.totalPrice span::text').get()
            dit['售价/万'] = Price
            unitPrice = li.css('.unitPrice span::text').get()
            dit['单价'] = unitPrice
            csv_writer.writerow(dit)

代码执行后,会把10个区的二手房信息爬取下来,我这里爬取的是3月13号的数据。

这是我爬下来的数据:

二、数据可视化

 这次可视化用鼓楼的数据为例,用pandas处理数据,pyecharts进行大图显示。

读取第一步中爬取的数据:

data = pd.read_csv('鼓楼二手房信息.csv',encoding='UTF-8')  #GB18030  , errors='ignore'
df = pd.DataFrame(data)

我这里做了五张可视化的图,分别是直方图显示单价区间的房源数,饼图显示面积区间内的房源数,饼状图显示总价,直方图显示房龄,直方图显示面积+价格区间内的房源数。

求区间内的房源数的方法是用pd.cut函数:

cuts = pd.cut(prices,range_num,labels=labes)

prices就是区域3000条房源信息的单价,range_num是设定的房价区间范围,我这里按2~2.4万,2.4~2.8万,4千一个区间分的,labes就是要在x轴显示的区间的标签。最终得到如下图:

从图可以看出鼓楼区学区房较多,房价在整个市区中最高,8万/平以上的就占8%。

按照区间统计数据的方法,统计出各个房屋面积区间段内的房源数量,及其占比,统计出5年内、5~10年等范围内房源数量。总价范围内的房源数量等等。不再赘述。

最后是groupby的方法,同时统计房屋面积+总价范围内的房源数量。

level_area = ['0-30平','30-60平','60-90平','90-120平','120-150平','150-200平','200-300平','300-400平','400-800平']
level_sales = ['150万以下','150-200万','200-300万','300-400万''400-500万','500-600万','600-800万','800-1000万','1千-4千万','大于4千万']
group = df.groupby(['面积区间','售价区间'],as_index=False)

groupby在sql语句中用到,pandas中也有该用法,得到的结果如下:

如果在这两个条件下匹配不到,就会返回nan,我们用group.size(),就能把groupby用到的条件和统计出的数量显示出来,剔除掉0的部分,就能得出面积、总价内的房源数量,虽然看起来无用,但是对于宁飘,有买房计划的人来说,多少大洋能买起一套房心中至少会有个底吧。

用该语句grouped[~grouped['size'].isin([0])],剔除结果为0的行。

这是去除没匹配到的结果: 

最后是把所有结果放到一张大图上显示:

page = Page(layout=Page.DraggablePageLayout)
page.add(bar_unitprice(),area_pie(),saleprice_pie(),hourseage_bar(),clusete_bar())page.render("test.html")
用到的是Page方法,先把可视化的函数写好,然后加到一张页面中,先保存成html文件,然后把各个视图拖动,拖动到你想要的地方,save config后,得到一个json文件,把该json文件放到目录下,注释掉render("test.html")
Page.save_resize_html("test.html",
                      cfg_file = "chart_config.json",
                      dest = "鼓楼二手房信息可视化.html")

就能得到所有可视化结果在一张图上的视图。

代码如下:

# -*- coding: utf-8 -*-
import pandas as pd
import csv
import random,re
from pyecharts.charts import Geo,Map,Bar, Line, Page,Pie, Boxplot, WordCloud
from pyecharts.globals import ChartType, SymbolType,ThemeType
from pyecharts import options as opts


data = pd.read_csv('鼓楼二手房信息.csv',encoding='UTF-8')  #GB18030  , errors='ignore'
df = pd.DataFrame(data)

def bar_unitprice():
    price_hourses = df['单价'].values.tolist()
    prices = []
    for price in price_hourses:
        price = float(price.replace("元/平","").replace(",",""))
        prices.append(price)
    labes = []
    range_num = []
    for i in range(20000,80001,4000):
        range_num.append(i)
        labes.append(str(i)+"-"+str(i+4000))
    range_num.append(200000)
    labes.pop(-1)
    labes.append("大于80000")
    # print(range_num,labes)
    cuts = pd.cut(prices,range_num,labels=labes)
    couts_range = cuts.value_counts().values.tolist()

    bar = Bar(init_opts=opts.InitOpts(width="2000px",height="700px",page_title="二手房价格区间房源数"))
    bar.add_xaxis(labes)
    bar.add_yaxis("", couts_range)
    bar.set_global_opts(
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=20)),
        title_opts=opts.TitleOpts(title="二手房价格区间房源数", subtitle="所"),
        datazoom_opts=opts.DataZoomOpts(),
    )

    return bar

def area_pie():
    area=(df['面积'].str.replace('平米','').astype(float))
    sum_a=area.sum()
    #fanwei=list(range(0,250,30))
    bins =[0,30,60,90,120,150,200,300,400,800]
    count=pd.cut(area.values,bins=bins,right=False)
    area_range=count.value_counts()#series,区间一个数
    #print(area_range)

    attr = ["[0-30)", "[30-60)", "[60-90)", "[90-120)", "[120-150)", "[150-200)", "[200-300)", "[300-400)",'[400-800)']
    area_range=list(area_range)
    pie = Pie()

    pie.add("", [list(z) for z in zip(attr, area_range)],
           radius=["30%", "75%"],
                center=["40%", "50%"],
                rosetype="radius")
    pie.set_global_opts(
                title_opts=opts.TitleOpts(title="房屋面积占比"),
                legend_opts=opts.LegendOpts(
                    type_="scroll", pos_left="80%", orient="vertical"
                ),
            ).set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{d}%"))
    return pie

def saleprice_pie():
    price_sale = df['售价/万'].values.tolist()
    labes = []
    range_num = []
    for i in range(100,1001,50):
        range_num.append(i)
        labes.append(str(i)+"-"+str(i+50))
    range_num.append(3000)
    labes.pop(-1)
    labes.append("大于1000")
    cuts = pd.cut(price_sale,range_num,labels=labes)
    price_range = cuts.value_counts().values.tolist()

    c = (
        Pie(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
        .add(
            "",
            [
                list(z)
                for z in zip(
                    labes ,
                    price_range ,
                )
            ],
            #设置圆心坐标
            center=["40%", "57%"],
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="总价内的房源数"),
            legend_opts=opts.LegendOpts(
                type_="scroll", pos_left="80%", orient="vertical",pos_top="15%"
            ),
        )
        .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
    )
    return c

def hourseage_bar():
    years = (df['建成年份'])
    years = (2022 - years.dropna(axis=0,how="all").astype(int)).values.tolist()
    labes = []
    range_num = []
    for i in range(0,22,3):
        range_num.append(i)
        labes.append(str(i)+"-"+str(i+3))
    range_num.append(70)
    labes.pop(-1)
    labes.append("大于21")
    cuts = pd.cut(years,range_num,labels=labes)
    hourse_range = cuts.value_counts().values.tolist()
    c = (
        Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))  # 设置主题
            .add_xaxis(labes)  # x轴为房龄
            .add_yaxis("房源数", hourse_range)  # y轴为房源数
            .set_global_opts(title_opts=opts.TitleOpts(title="各个房龄的房子"))
            .set_series_opts(
            label_opts=opts.LabelOpts(is_show=False),
            # 插入平均值线
            markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(type_="average", name="平均值"), ]),
            # 插入最大值最小值点
            markpoint_opts=opts.MarkPointOpts(data=[
                opts.MarkPointItem(type_="max", name="最大值"),
                opts.MarkPointItem(type_="min", name="最小值"),
            ])
        )
    )
    return c

def clusete_bar():
    bins =[0,30,60,90,120,150,200,300,400,800]
    level_area = ['0-30平','30-60平','60-90平','90-120平','120-150平','150-200平','200-300平','300-400平','400-800平']
    sales_step = [0,150,200,300,400,500,600,800,1000,4000]
    level_sales = ['150万以下','150-200万','200-300万','300-400万''400-500万','500-600万','600-800万','800-1000万','1千-4千万','大于4千万']
    years_step = [0,5,10,15,20,25,70]
    level_year = ['5年以下','5-10年','10-15年','15-20年','20-25年','20年以上']
    area=(df['面积'].str.replace('平米','').astype(float))
    price_sale = (df['售价/万']).astype(float)
    years = (df['建成年份'])    #.fillna(0.inplace=True)
    years = years.fillna(1000)
    years = (2022 - years.astype(int))

    df["面积区间"] = pd.cut(area,bins=bins,labels=level_area)
    df["房龄"] = pd.cut(years,bins=years_step,labels=level_year)
    df["售价区间"] = pd.cut(price_sale,bins=sales_step,labels=level_sales,right=False)
    # df2 =  df[['面积区间', '房龄' , '售价区间','标题']]
    group = df.groupby(['面积区间','售价区间'],as_index=False)
    # test = group.get_group(('60-90平','200-300万','5年以下'))[['售价/万','面积','房龄','标题']]
    grouped = group.size()
    grouped=grouped[~grouped['size'].isin([0])]
    cluster = grouped.values.tolist()
    # grouped.drop( index = grouped.size[grouped.size == 0].index )
    counts = []
    lables = []
    for i in range(len(cluster)):
        counts.append(cluster[i][2])
        lables.append(str(cluster[i][0]+","+cluster[i][1]))
    bar = Bar(init_opts=opts.InitOpts(width="2000px",height="700px",page_title="总价+面积聚类",theme=ThemeType.LIGHT))
    bar.add_xaxis(lables)
    bar.add_yaxis("", counts)
    bar.set_global_opts(
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=20)),
        title_opts=opts.TitleOpts(title="总价+面积聚类", subtitle="所"),
        datazoom_opts=opts.DataZoomOpts(),
    )
    return bar

page = Page(layout=Page.DraggablePageLayout)
page.add(bar_unitprice(),area_pie(),saleprice_pie(),hourseage_bar(),clusete_bar())
# page.render("test.html")
Page.save_resize_html("test.html",
                      cfg_file = "chart_config.json",
                      dest = "鼓楼二手房信息可视化.html")

得到的大图如下:

 关注页面的小伙伴们可以学习上面大图可视化的方法。

如果只是想关注下数据,那看下面清晰的结果:

  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

映之123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值