Python爬取房屋租售信息

緣起

第一次接觸「租售比」這個概念是在知乎 團支書 對 如何經過房屋租售比來判斷房產的價值或泡沫? 這個問題的回答上看到的,當時看到她搞出來的一些圖和分析就感受頗有意思,尋思着本身也能夠嘗試用相同的方法對北京的房屋租售信息作一個分析。沒想到一搜索,相似的東西早就有人作出來了。好比在知乎上的這個問題:北京在哪裏租房的居住性價比高?。高票答案雖然來自鏈家旗下自如的員工,可是其以熱力圖一目瞭然的展現出了北京出租房房租的高低分佈狀況,難怪下面會有知友回覆說:廣告滿分。而後還有這篇文章【租房數據分析】2016年在北京如何租到好房子?,再加上今年這剛一開始,北京、上海的房價又開始一輪瘋漲,天然就更讓你們開始關注房產有關的信息。因而,我的也嘗試着把這個事情搞一搞。javascript

處理流程

首先來看處理流程,其實就是一個數據收集、處理、分析和展現的過程。php

對於數據收集,58同城、趕集網、鏈家網這些上面有着豐富的信息,只不過對於前面兩個網站,如今,哦,可能一直都是這樣,已經差很少都被中介信息給淹沒了。因此即便這些網站已經提供了大量的信息,可是若是真的想拿來使用,仍是要進行一些處理。html

對於數據分析,相信每一個人都有本身獨特的視角,一樣一份數據,不一樣的人有着不一樣的關注點,因此很難制定統一的規則,這裏就單從價格這一方面來作一個統計。java

對於數據展現,由於要看的是一個城市的數據,所以熱力圖是一個很好的選擇,而百度地圖已經提供了很方便的接口,因此這裏使用百度的地圖接口來進行熱力圖的展現。git

由此,處理流程大體分爲一下幾個步驟:github

  1. 選定某個城市
  2. 抓取區域信息
  3. 制定翻頁規則
  4. 抓取租售信息
  5. 獲取位置信息
  6. 生成價格熱圖

詳細流程

這裏以趕集網的二手房銷售信息爲例,演示如何抓取數據並將結果展現到熱力圖上。web

頁面分析

首先打開 趕集網北京站,能夠看到,排在左上角的就是」北京房產」, 由這個位置就能夠想見,房地產相關的業務得佔了它全站多大的比例。json

咱們直接點擊 二手房 來看一下這個頁面,發現上面給出了北京市的幾個區域,方便用戶在特定城區查找房源。api

若是你看過知乎上的這個問答:如何不吹牛地形容北京有多大?,那麼你必定會理解對於帝都來講,只給出一個城區那範圍有點太大了。所幸,這些網站都在某個區域下給出了更詳細分區。app

頁面的下半部分,是各大中介發佈的出售信息,先別爲使人咋舌的房價感慨,隨便點開一條信息,進入到詳細頁面,能夠看到裏面把總價、單價、戶型、樓層、小區、位置等都描述的很是清楚了,有點甚至連首付和月供都已經幫你算好了。這麼豐富的信息,基本上已經知足了從多個維度對房產信息進行分析統計。

可是做爲演示,暫時沒有必要搞那麼複雜,這裏只須要統計一下每一個小區的均價,給出按照小區價格的熱力圖就能夠了。

在詳細頁面裏給出了該條出售信息的房源所屬的小區,點擊進入小區的詳細頁面,能夠看到小區有着這樣一個結構。

點擊上一級頁面,咱們看到有關小區的列表頁,並且這裏已經給出了某個小區的均價。

由此咱們能夠制定咱們的抓取策略。首先咱們抓取某個城市的區域信息,而後抓取某個區域下面的子區域信息,最後再抓取某個子區域下的小區信息。

這裏還有一個問題須要注意,某個區域下的小區列表可能不只僅只有一頁,因此還要處理翻頁。

由圖便可看到翻頁的規律。

數據抓取

這裏使用最簡單的方式-基於 urllib2 進行頁面抓取

1
2
3
4
5
6

import urllib2
response = urllib2.urlopen('http://bj.ganji.com/xiaoqu')
html = response.read()
with open('index.html', 'w') as html_file:
html_file.write(html)

打開抓取的頁面,使用Chrome開發者工具HTML進行分析

由此便可根據 XPath 肯定城市下面的區域選擇器爲

1
area_select = '//dl[@class="selitem selitem-area lh24 clearfix"]//div[@class=" clearfix"]/a[@rel="nofollow"]'

lxml做爲解析HTML的工具,嘗試獲取城市的區域信息

1
2
3
4
5
6
7
8
from lxml import etree

html = file('index.html').read().decode('UTF-8')
doc = etree.HTML(html)
area_select = '//dl[@class="selitem selitem-area lh24 clearfix"]//div[@class=" clearfix"]/a[@rel="nofollow"]'
area_list = doc.xpath(area_select)
for area in area_list:
print area.xpath('string()'), area.get('href')

使用相同的方式,便可實現對某個區域下子區域信息的抓取,這裏以海淀區爲例

1
2
3
4
5
6
7
8
9
10
import urllib2
from lxml import etree

response = urllib2.urlopen('http://bj.ganji.com/xiaoqu/d0')
html = response.read()
doc = etree.HTML(html)
subarea_select = '//dl[@class="selitem selitem-area lh24 clearfix"]//div[@class="subarea clearfix"]/a[@rel="nofollow"]'
subarea_list = doc.xpath(subarea_select)
for subarea in subarea_list:
print subarea.xpath('string()'), subarea.get('href')

再來試試對某個子區域下小區信息的抓取,這裏以宇宙中心-五道口爲例

1
2
3
4
5
6
7
8
9
10
11
12
import urllib2
from lxml import etree

response = urllib2.urlopen('http://bj.ganji.com/xiaoqu/d0s11')
html = response.read()
doc = etree.HTML(html)
community_select = '//div[@class="listBox"]/ul/li]'
community_list = doc.xpath(community_select)
for community in community_list:
name = community.xpath('./div[@class="list-mod2"]/div[@class="info-title"]/a')[0].xpath('string()')
price = community.xpath('./div[@class="list-mod3 xq-price clearfix"]/p/b')[0].xpath('string()')
print name, price

數據展現

數據有了,接下來就是如何展現。

首先查看一下百度地圖的JavaScript API,裏面提到

該套API免費對外開放。自v1.5版本起,您需先申請密鑰(ak)纔可以使用,接口(除發送短信功能外)無使用次數限制。

因此,註冊一個賬號,而後申請密鑰便可。

文檔裏已經給出了一個熱力圖示例,經過源碼能夠看到關鍵在於定義點座標。

1
2
3
4
5
var points =[
{"lng":116.418261,"lat":39.921984,"count":50},
{"lng":116.423332,"lat":39.916532,"count":51},
{"lng":116.419787,"lat":39.930658,"count":15},
...]

好在百度開放平臺提供了Geocoding API

Geocoding API 是一類簡單的HTTP接口,用於提供從地址到經緯度座標或者從經緯度座標到地址的轉換服務,用戶可使用C# 、C++、Java等開發語言發送HTTP請求且接收JSON、XML的返回數據。

測試一下

1
2
3
4
5
6
7
8
import urllib2

ak = '密鑰'
city = '北京市'
address = '華清嘉園'
api_url = 'http://api.map.baidu.com/geocoder/v2/?ak=%s&output=json&city=%s&address=%s' % (ak, city, address)
result = urllib2.urlopen(api_url).read()
print result

返回結果格式以下

1
2
3
4
5
6
7
8
9
10
{"status": 0,
"result": {
"location": {
"lng": 116.34213332629,
"lat": 39.997261285991
},
"precise": 0,
"confidence": 60,
"level": "地產小區"
}}

有了它就能夠批量完成已抓取小區的座標轉換了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import urllib2
import json

def get_coordinate(address, ak='密鑰', city='北京市'):
api_url = 'http://api.map.baidu.com/geocoder/v2/?ak=%s&output=json&city=%s&address=%s' % (ak, city, address)
result = urllib2.urlopen(api_url).read()
return json.loads(result)

def transform(community_data):
points = []
for name, price in community_data.iteritems():
obj = get_coordinate(name)
if obj['status'] != 0:
continue
d = obj['result']['location']
d['price'] = float(price)
points.append(d)
with open('data/heatmap_data.js', 'w') as jsfile:
jsfile.write('var points = ' + json.dumps(points) + ';')

數據有了,如今只須要把熱力圖示例中給出的頁面修改一下,就能夠顯示抓取到的結果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 這裏要添加本身的密鑰 -->
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=密鑰"></script>
<!-- 導入生成的js文件 -->
<script type="text/javascript" src="data/heatmap_data.js"></script>

<script type="text/javascript">
// 註銷掉示例數據
// var points = [
{"lng":116.418261,"lat":39.921984,"count":50},
{"lng":116.423332,"lat":39.916532,"count":51},
{"lng":116.419787,"lat":39.930658,"count":15},
...];
</script>

功能測試

根據趕集網的頁面規則,測試一下抓取效果

1
2
3
4
5
6
7
8
9
10
11
index_url = 'http://bj.ganji.com/xiaoqu'
create_new = False
city_model = CityModel(index_url=index_url, create_new=create_new)

page_select = '//div[@class="pageBox"]/ul/li/a'
community_select = '//div[@class="listBox"]/ul/li'
name_select = './div[@class="list-mod2"]/div[@class="info-title"]/a'
price_select = './div[@class="list-mod3 xq-price clearfix"]/p/b'

city_model.get_all_community(page_select, community_select, name_select, price_select)
city_model.save()

完整代碼參見: house_spider

注意事項

既然是爬取別人網站上的數據,固然道德也很重要,咱們來看一下趕集網Robots規則

1
2
3
4
5
6
7
8
9
10
11
User-agent: *
Disallow: /tel/*.png
Disallow: /*/?navtab=
Disallow: /404.html
Disallow: /findjob/send_resume.php
Disallow: /user/
Disallow: /misc/
Disallow: /zq_biyeji/
Disallow: /utils/
Disallow: /pub/
Disallow: /sorry/

還好咱們只是抓取租售信息,這些都並不在屏蔽之列,只要不是抓的太狠(間隔時間太短),仍是能夠基於此嘗試作一些有趣的統計和分析的。

 

相關文章
相關標籤/搜索