如今年輕人到25歲+,總的要考慮買房結婚的問題,2016年的一波房價大漲,小夥伴們紛紛表示再也買不起上海的房產了,博主也得考慮考慮將來的發展了,思考了好久,決定去杭州工做、買房、定居、生活,以前去過不少次杭州,很喜歡這個城市,因而例行天天晚上都要花一點時間關注杭州的房產銷售狀況,以及價格,起初我天天都在杭州的本地論壇,透明售房網上查看,每一天的房產銷售數據,可是無奈博主不是杭州本地人,看了網頁上展現的不少樓盤,可是我不知道都在什麼地方啊,因而乎,看到價格合適的,老是到高德地圖去搜索地理位置,每次很是麻煩,因而我想是否是能夠,寫一個小的爬蟲工具,天天抓取透明售房網上的銷售記錄,直接展現在地圖上,直觀明瞭的看看都是哪些地方的樓盤地理位置不錯,同時價格也在能接受的範圍內,同時最近在學習node.js,正好能夠練練手。說幹就幹,一個下午時間,有了初步的成果以下,後期在加入天天的銷售數據,加入到mongoDB中,用於分析每週、每個月的銷售數據,用於本身買房的參考,要學以至用嘛!javascript
先說下基本思路:css
第一步:利用nodejs,技術抓取透明售房網的實時的數據(http://www.tmsf.com/daily.htm),存儲在後臺;html
第二步:頁面請求後臺數據,而後藉助高德地圖提供的按照名稱查詢地理位置的服務,展現在地圖上,並綁定每一個樓盤的銷售詳情;java
ok,有了基本思路,下面一步一步的開幹:node
一:後臺爬蟲react
1.抓取在線網絡數據git
這裏先介紹一個利器,cheerio(https://github.com/cheeriojs/cheerio),能夠說是位服務器特別定製的,快速,靈活,實施的jQuery核心實現,或者說是後臺解析html的;安裝nodejs 模塊這裏再也不說明,抓取html頁面邏輯比較簡單,直接上代碼: github
1 //定義爬蟲數據源網絡地址 2 var url = 'http://www.tmsf.com/daily.htm'; 3 4 /** 5 * 請求網絡地址抓取數據 6 * @param {function} callBack 傳回爬蟲數據處理以後的最終結果 7 */ 8 function getHzfcSaleInfo(callBack) { 9 var hzfcSaleInfo = []; 10 http.get(url, function(res) { 11 var html = ''; 12 res.on('data', function(data) { 13 html += data; 14 }); 15 res.on('end', function() { 16 hzfcSaleInfo = filterData(html); 17 callBack(hzfcSaleInfo); 18 }); 19 res.on('error', function() { 20 console.log('獲取數據出錯'); 21 }); 22 }) 23 }
2.解析獲取的數據web
已經抓取整個網頁的數據,在這一步中要根據網頁的DOM,結構來分析應該怎麼解析:首先咱們能夠看到,每日房產銷售狀況的數據是分行政區展現在並列的幾個div中,經過display控制顯示哪個行政區,因此思路就是首先獲取這個外層container,而後不停一層一層的循環解析數據;ajax
其中解析到每一行的數據的時候,發現了一個有點奇葩的網頁展現,每一行後面數字居然不是直接用數字來表示的,而是用css的圖片來代替,可能就是爲了防止我這種爬蟲的吧,不過無論了,有了css,還不能轉成數字嗎,哈哈
具體代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
/**
* 解析DOM節點,提取核心數據
* @param {string} html 頁面總體html
* @returns {array} 最終處理以後的數據
*/
function
filterData(html) {
var
$ = cheerio.load(html);
var
data = [];
var
container = $(
'#myCont2'
)
var
districts = container.find(
'table'
);
districts.each(
function
() {
var
district = $(
this
);
var
trs = district.find(
'tr'
);
trs.each(
function
() {
var
tr = $(
this
);
var
tds = tr.find(
'td'
);
var
i = 0;
var
estateName;
var
estateSite;
var
estateSign;
var
estateReserve;
var
estateArea;
var
estatePrice;
tds.each(
function
() {
var
col = $(
this
);
if
(i == 0) {
estateName = col.find(
'a'
).text();
}
else
if
(i == 1) {
estateSite = col.text().replace(/[^\u4e00-\u9fa5]/gi,
""
);
}
else
if
(i == 2) {
var
spanClass =
''
;
var
spans = col.find(
'span'
);
spans.each(
function
(a) {
var
span = $(
this
);
var
cssName = classNameToNumb(span.attr(
'class'
));
spanClass = spanClass + cssName;
});
estateSign = spanClass;
}
else
if
(i == 3) {
var
spanClass =
''
;
var
spans = col.find(
'span'
);
spans.each(
function
(a) {
var
span = $(
this
);
var
cssName = classNameToNumb(span.attr(
'class'
));
spanClass = spanClass + cssName;
});
estateReserve = spanClass;
}
else
if
(i == 4) {
var
spanClass =
''
;
var
spans = col.find(
'span'
);
spans.each(
function
(a) {
var
span = $(
this
);
var
cssName = classNameToNumb(span.attr(
'class'
));
spanClass = spanClass + cssName;
});
estateArea = spanClass +
'㎡'
;
}
else
if
(i == 5) {
var
spanClass =
''
;
var
spans = col.find(
'span'
);
spans.each(
function
(a) {
var
span = $(
this
);
var
cssName = classNameToNumb(span.attr(
'class'
));
spanClass = spanClass + cssName;
});
estatePrice = spanClass +
'元/㎡'
;
}
i++;
})
var
estateData = {
estateName: estateName,
estateSite: estateSite,
estateSign: estateSign,
estateReserve: estateReserve,
estateArea: estateArea,
estatePrice: estatePrice
}
if
(estateData.estateName) {
data.push(estateData);
}
})
})
return
data;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/**
* 根據class name 提取數值
* @param {string} className 節點class name
* @returns 數值
*/
function
classNameToNumb(className) {
var
numb;
if
(className ==
'numbzero'
) {
numb =
'0'
;
}
else
if
(className ==
'numbone'
) {
numb =
'1'
;
}
else
if
(className ==
'numbtwo'
) {
numb =
'2'
;
}
else
if
(className ==
'numbthree'
) {
numb =
'3'
;
}
else
if
(className ==
'numbfour'
) {
numb =
'4'
;
}
else
if
(className ==
'numbfive'
) {
numb =
'5'
;
}
else
if
(className ==
'numbsix'
) {
numb =
'6'
;
}
else
if
(className ==
'numbseven'
) {
numb =
'7'
;
}
else
if
(className ==
'numbeight'
) {
numb =
'8'
;
}
else
if
(className ==
'numbnine'
) {
numb =
'9'
;
}
else
if
(className ==
'numbdor'
) {
numb =
'.'
;
}
return
numb;
}
|
數據抓取的最終結果,先作個簡單的展現:
二:頁面展現
1.搭建基本的web server,爲了方便使用的是express(http://www.expressjs.com.cn/)框架,直接上代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
var
express = require(
'express'
);
var
getHzfcSaleInfo = require(
'./hzfc'
);
var
app = express();
app.use(express.
static
(
'public'
));
//處理前臺頁面的數據請求
app.get(
'/getHzfcSaleInfo'
,
function
(req, res) {
/**
* 處理前臺頁面ajax請求
* 返回給前臺所有的處理數據
* @param {any} data
*/
var
hzfcSaleInfo = getHzfcSaleInfo(
function
(data) {
res.end(JSON.stringify({ data: data }));
// data.forEach(function(item) {
// if (item.estateName) {
// console.log(item.estateName + ' ' + item.estateSite + ' ' + item.estateSign + ' ' + item.estateReserve + ' ' + item.estateArea + ' ' + item.estatePrice + '\n');
// }
// })
});
//res.end(hzfcSaleInfo);
});
/**
* 啓動web server
*/
var
server = app.listen(8081,
function
() {
console.log(
'web server start success'
,
'訪問地址爲:http://localhost:8081/index.html'
);
})
|
其中app.get方法用來處理前臺頁面的請求
2.前臺頁面展現:
首先利用高德地圖API(http://lbs.amap.com/api/javascript-api/summary/),在網頁中展現黑色的地圖底圖,而後頁面發送請求給後臺請求數據,而後利用高德api的由名稱查詢地理位置的方法,遞歸請求每一個樓盤的地理位置,而後用marker添加到地圖上,
代碼以下:
結束語:
這只是個初步的版本,很簡單的展現天天都的銷售狀況,全部的代碼都託管在了GITHUB上,項目地址爲:https://github.com/react-map/HangzhouRealEstate,各路小夥伴若是有新的思路,新的想法,能夠直接在Issues上提出來,一塊兒作一個房產銷售數據可視化的平臺。