手把手教你用Pandas分析全國(guó)城市房?jī)r(jià)

導(dǎo)讀:Pandas靈活好用,能夠完成復(fù)雜的、重復(fù)的、批量的數(shù)據(jù)處理。本文教你利用Pandas爬取房?jī)r(jià),以及分析全國(guó)城市的房?jī)r(jià)。

01 利用爬蟲(chóng)獲取房?jī)r(jià)
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的類改造函數(shù),再用鏈?zhǔn)椒椒ㄕ{(diào)用:
# 爬蟲(chóng)類
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à)可以從以下網(wǎng)址獲取:
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ù)類型:
dfr.dtypes
'''
序號(hào) int64
城市名稱 object
平均單價(jià)(元/㎡) object
環(huán)比 object
同比 object
dtype: object
'''數(shù)據(jù)都是object類型,需要對(duì)數(shù)據(jù)進(jìn)行提取和類型轉(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à)綜合樣式圖
關(guān)于作者:李慶輝,數(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)用水平。
本文摘編自《深入淺出Pandas:利用Python進(jìn)行數(shù)據(jù)處理與分析》,經(jīng)出版方授權(quán)發(fā)布。

推薦語(yǔ):這是一本全面覆蓋了Pandas使用者的普遍需求和痛點(diǎn)的著作,基于實(shí)用、易學(xué)的原則,從功能、使用、原理等多個(gè)維度對(duì)Pandas做了全方位的詳細(xì)講解,既是初學(xué)者系統(tǒng)學(xué)習(xí)Pandas難得的入門(mén)書(shū),又是有經(jīng)驗(yàn)的Python工程師案頭必不可少的查詢手冊(cè)。
