手把手教你用Pandas分析全國(guó)城市房?jī)r(jià)
本文轉(zhuǎn)載自微信公眾號(hào)「數(shù)倉(cāng)寶貝庫(kù)」,作者李慶輝。轉(zhuǎn)載本文請(qǐng)聯(lián)系數(shù)倉(cāng)寶貝庫(kù)公眾號(hào)。
Pandas在配合做網(wǎng)絡(luò)數(shù)據(jù)采集爬蟲(chóng)時(shí),也能發(fā)揮其優(yōu)勢(shì),可承擔(dān)數(shù)據(jù)調(diào)用、數(shù)據(jù)存儲(chǔ)的工作。將數(shù)據(jù)存入DataFrame后,可直接進(jìn)入下一步分析。本例以獲取某房產(chǎn)網(wǎng)站中房?jī)r(jià)為目標(biāo),來(lái)體驗(yàn)一下Pandas的便捷之處。
首先利用requests(需要安裝)庫(kù)獲取單個(gè)小區(qū)的平均價(jià)格:
- import requests # 安裝:pip install requests
- # 創(chuàng)建一個(gè)Session
- s = requests.Session()
- # 訪問(wèn)小區(qū)頁(yè)面
- xq = s.get('https://bj.lianjia.com/xiaoqu/1111027382589/')
- # 查看頁(yè)面源碼
- xq.text
- # 找到價(jià)格位置附近的源碼為:
- # <span class="xiaoquUnitPrice">95137</span>
- # 切分與解析
- xq.text.split('xiaoquUnitPrice">')[1].split('</span>')[0]
- # '93754'
最終得到這個(gè)小區(qū)的平均房?jī)r(jià)。這里使用了將目標(biāo)信息兩邊的信息進(jìn)行切片、形成列表再讀取的方法。也可以用第三方庫(kù)Beautiful Soup 4來(lái)解析。Beautiful Soup是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的Python庫(kù),它能夠通過(guò)解析源碼來(lái)方便地獲取指定信息。
我們構(gòu)建獲取小區(qū)名稱和平均房?jī)r(jià)的函數(shù):
- # 獲取小區(qū)名稱的函數(shù)
- def pa_name(x):
- xq = s.get(f'https://bj.lianjia.com/xiaoqu/{x}/')
- name = xq.text.split('detailTitle">')[1].split('</h1>')[0]
- return name
- # 獲取平均房?jī)r(jià)的函數(shù)
- def pa_price(x):
- xq = s.get(f'https://bj.lianjia.com/xiaoqu/{x}/')
- price = xq.text.split('xiaoquUnitPrice">')[1].split('</span>')[0]
- return price
接下來(lái)利用Pandas執(zhí)行爬蟲(chóng)獲取信息:
- # 小區(qū)列表
- xqs = [1111027377595, 1111027382589,
- 1111027378611, 1111027374569,
- 1111027378069, 1111027374228,
- 116964627385853]
- # 構(gòu)造數(shù)據(jù)
- df = pd.DataFrame(xqs, columns=['小區(qū)'])
- # 爬取小區(qū)名
- df['小區(qū)名'] = df.小區(qū).apply(lambda x: pa_name(x))
- # 爬取房?jī)r(jià)
- df['房?jī)r(jià)'] = df.小區(qū).apply(lambda x: pa_price(x))
- # 查看結(jié)果
- df
- '''
- 小區(qū) 小區(qū)名 房?jī)r(jià)
- 0 1111027377595 瞰都國(guó)際 73361
- 1 1111027382589 棕櫚泉國(guó)際公寓 93754
- 2 1111027378611 南十里居 56459
- 3 1111027374569 觀湖國(guó)際 88661
- 4 1111027378069 麗水嘉園 76827
- 5 1111027374228 泛海國(guó)際碧海園 97061
- 6 116964627385853 東山condo 145965
- '''
可以先用Python的類(lèi)改造函數(shù),再用鏈?zhǔn)椒椒ㄕ{(diào)用:
- # 爬蟲(chóng)類(lèi)
- class PaChong(object):
- def __init__(self, x):
- self.s = requests.session()
- self.xq = self.s.get(f'https://bj.lianjia.com/xiaoqu/{x}/')
- self.name = self.xq.text.split('detailTitle">')[1].split('</h1>')[0]
- self.price = self.xq.text.split('xiaoquUnitPrice">')[1].split('</span>')[0]
- # 爬取數(shù)據(jù)
- (
- df
- .assign(小區(qū)名=df.小區(qū).apply(lambda x: PaChong(x).name))
- .assign(房?jī)r(jià)=df.小區(qū).apply(lambda x: PaChong(x).price))
- )
以上網(wǎng)站可能會(huì)改版,代碼不適用時(shí)需要調(diào)整爬蟲(chóng)代碼。
02全國(guó)城市房?jī)r(jià)分析
中國(guó)主要城市的房?jī)r(jià)可以從https://www.creprice.cn/rank/index.html獲取。該網(wǎng)頁(yè)中會(huì)顯示上一個(gè)月的房?jī)r(jià)排行情況,先復(fù)制前20個(gè)城市的數(shù)據(jù),然后使用pd.read_clipboard()讀取。我們來(lái)分析一下該月的數(shù)據(jù)(下例中用的是2020年10月數(shù)據(jù))。
- import pandas as pd
- import matplotlib.pyplot as plt
- plt.rcParams['figure.figsize'] = (8.0, 5.0) # 固定顯示大小
- plt.rcParams['font.family'] = ['sans-serif'] # 設(shè)置中文字體
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 設(shè)置中文字體
- plt.rcParams['axes.unicode_minus'] = False # 顯示負(fù)號(hào)
- dfr = pd.read_clipboard()
- # 取源數(shù)據(jù)
- dfr.head()
- '''
- 序號(hào) 城市名稱 平均單價(jià)(元/㎡) 環(huán)比 同比
- 0 1 深圳 78,722 +2.61% +20.44%
- 1 2 北京 63,554 -0.82% -1.2%
- 2 3 上海 58,831 +0.4% +9.7%
- 3 4 廈門(mén) 48,169 -0.61% +9.52%
- 4 5 廣州 38,351 -1.64% +13.79%
- '''
查看數(shù)據(jù)類(lèi)型:
- dfr.dtypes
- '''
- 序號(hào) int64
- 城市名稱 object
- 平均單價(jià)(元/㎡) object
- 環(huán)比 object
- 同比 object
- dtype: object
- '''
數(shù)據(jù)都是object類(lèi)型,需要對(duì)數(shù)據(jù)進(jìn)行提取和類(lèi)型轉(zhuǎn)換:
- df = (
- # 去掉千分位符并轉(zhuǎn)為整型
- dfr.assign(平均單價(jià)=dfr['平均單價(jià)(元/㎡)'].str.replace(',','').astype(int))
- .assign(同比=dfr.同比.str[:-1].astype(float)) # 去百分號(hào)并轉(zhuǎn)為浮點(diǎn)型
- .assign(環(huán)比=dfr.環(huán)比.str[:-1].astype(float)) # 去百分號(hào)并轉(zhuǎn)為浮點(diǎn)型
- .loc[:,['城市名稱','平均單價(jià)','同比','環(huán)比']] # 重命名列
- )
- df.head()
- '''
- 城市名稱 平均單價(jià) 同比 環(huán)比
- 0 深圳 78722 20.44 2.61
- 1 北京 63554 -1.20 -0.82
- 2 上海 58831 9.70 0.40
- 3 廈門(mén) 48169 9.52 -0.61
- 4 廣州 38351 13.79 -1.64
- '''
接下來(lái)就可以對(duì)整理好的數(shù)據(jù)進(jìn)行分析了。首先看一下各城市的均價(jià)差異,數(shù)據(jù)順序無(wú)須再調(diào)整,代碼執(zhí)行效果如圖1所示。
- (
- df.set_index('城市名稱')
- .平均單價(jià)
- .plot
- .bar()
- )
圖1 各城市平均房?jī)r(jià)
各城市平均房?jī)r(jià)同比與環(huán)比情況如圖2所示。
- (
- df.set_index('城市名稱')
- .loc[:, '同比':'環(huán)比']
- .plot
- .bar()
- )
圖2 各城市平均房?jī)r(jià)同比和環(huán)比
將同比與環(huán)比的極值用樣式標(biāo)注,可見(jiàn)東莞異常突出,房?jī)r(jià)同比、環(huán)比均大幅上升,如圖3所示。
- (
- df.style
- .highlight_max(color='red', subset=['同比', '環(huán)比'])
- .highlight_min(subset=['同比', '環(huán)比'])
- .format({'平均單價(jià)':"{:,.0f}"})
- .format({'同比':"{:2}%", '環(huán)比':"{:2}%"})
- )
圖3 各城市平均房?jī)r(jià)變化樣式圖
繪制各城市平均單價(jià)條形圖,如圖4所示。
- # 條形圖
- (
- df.style
- .bar(subset=['平均單價(jià)'], color='yellow')
- )
圖4 各城市平均單價(jià)樣式圖
將數(shù)據(jù)樣式進(jìn)行綜合可視化:將平均單價(jià)背景色設(shè)為漸變,并指定色系BuGn;同比、環(huán)比條形圖使用不同色系,且以0為中點(diǎn),體現(xiàn)正負(fù);為比值加百分號(hào)。最終效果如圖5所示。
- (
- df.style
- .background_gradient(subset=['平均單價(jià)'], cmap='BuGn')
- .format({'同比':"{:2}%", '環(huán)比':"{:2}%"})
- .bar(subset=['同比'],
- color=['#ffe4e4','#bbf9ce'], # 上漲、下降的顏色
- vmin=0, vmax=15, # 范圍定為以0為基準(zhǔn)的上下15
- align='zero'
- )
- .bar(subset=['環(huán)比'],
- color=['red','green'], # 上漲、下降的顏色
- vmin=0, vmax=11, # 范圍定為以0為基準(zhǔn)的上下11
- align='zero'
- )
- )
圖5 各城市平均房?jī)r(jià)綜合樣式圖
本文摘編于《深入淺出Pandas:利用Python進(jìn)行數(shù)據(jù)處理與分析》,經(jīng)出版方授權(quán)發(fā)布。
作者:李慶輝,數(shù)據(jù)產(chǎn)品專家,某電商公司數(shù)據(jù)產(chǎn)品團(tuán)隊(duì)負(fù)責(zé)人,擅長(zhǎng)通過(guò)數(shù)據(jù)治理、數(shù)據(jù)分析、數(shù)據(jù)化運(yùn)營(yíng)提升公司的數(shù)據(jù)應(yīng)用水平。