node.js爬蟲杭州房產銷售及數據可視化

     如今年輕人到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添加到地圖上,

     代碼以下:

 View Code

 

    結束語:

   這只是個初步的版本,很簡單的展現天天都的銷售狀況,全部的代碼都託管在了GITHUB上,項目地址爲:https://github.com/react-map/HangzhouRealEstate,各路小夥伴若是有新的思路,新的想法,能夠直接在Issues上提出來,一塊兒作一個房產銷售數據可視化的平臺。

相關文章
相關標籤/搜索