node.js爬虫杭州房产销售及数据可视化

现在年轻人到25岁+,总的要考虑买房结婚的问题,2016年的一波房价大涨,小伙伴们纷纷表示再也买不起上海的房产了,博主也得考虑考虑未来的发展了,思考了很久,决定去杭州工作、买房、定居、生活,之前去过很多次杭州,很喜欢这个城市,于是例行每天晚上都要花一点时间关注杭州的房产销售情况,以及价格,起初我每天都在杭州的本地论坛,透明售房网上查看,每一天的房产销售数据,但是无奈博主不是杭州本地人,看了网页上展示的很多楼盘,但是我不知道都在什么地方啊,于是乎,看到价格合适的,总是到高德地图去搜索地理位置,每次非常麻烦,于是我想是不是可以,写一个小的爬虫工具,每天抓取透明售房网上的销售记录,直接展示在地图上,直观明了的看看都是哪些地方的楼盘地理位置不错,同时价格也在能接受的范围内,同时最近在学习node.js,正好可以练练手。说干就干,一个下午时间,有了初步的成果如下,后期在加入每天的销售数据,加入到mongoDB中,用于分析每周、每月的销售数据,用于自己买房的参考,要学以致用嘛!

先说下基本思路:

第一步:利用nodejs,技术抓取透明售房网的实时的数据(http://www.tmsf.com/daily.htm),存储在后台;

第二步:页面请求后台数据,然后借助高德地图提供的按照名称查询地理位置的服务,展示在地图上,并绑定每个楼盘的销售详情;

ok,有了基本思路,下面一步一步的开干:

一:后台爬虫

1.抓取在线网络数据

这里先介绍一个利器,cheerio(https://github.com/cheeriojs/cheerio),可以说是位服务器特别定制的,快速,灵活,实施的jQuery核心实现,或者说是后台解析html的;安装nodejs 模块这里不再说明,抓取html页面逻辑比较简单,直接上代码:

 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.解析获取的数据

已经抓取整个网页的数据,在这一步中要根据网页的DOM,结构来分析应该怎么解析:首先我们可以看到,每日房产销售情况的数据是分行政区展示在并列的几个div中,通过display控制显示哪一个行政区,所以思路就是首先获取这个外层container,然后不停一层一层的循环解析数据;

其中解析到每一行的数据的时候,发现了一个有点奇葩的网页展示,每一行后面数字竟然不是直接用数字来表示的,而是用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上提出来,一起做一个房产销售数据可视化的平台。

时间: 2024-10-03 05:40:42

node.js爬虫杭州房产销售及数据可视化的相关文章

Node.js爬虫-爬取慕课网课程信息

第一次学习Node.js爬虫,所以这时一个简单的爬虫,Node.js的好处就是可以并发的执行 这个爬虫主要就是获取慕课网的课程信息,并把获得的信息存储到一个文件中,其中要用到cheerio库,它可以让我们方便的操作HTML,就像是用jQ一样 开始前,记得 npm install cheerio 为了能够并发的进行爬取,用到了Promise对象 //接受一个url爬取整个网页,返回一个Promise对象 function getPageAsync(url){ return new Promise(

Node.js abaike图片批量下载Node.js爬虫1.00版

这个与前作的差别在于地址的不规律性,需要找到下一页的地址再爬过去找. //====================================================== // abaike图片批量下载Node.js爬虫1.00 // 2017年11月9日 //====================================================== // 内置http模块 var http=require("http"); // 内置文件处理模块,用于创

Node.js aitaotu图片批量下载Node.js爬虫1.00版

即使是https网页,解析的方式也不是一致的,需要多试试. 代码: //====================================================== // aitaotu图片批量下载Node.js爬虫1.00 // 2017年11月14日 //====================================================== // 内置http模块 var https=require("https"); // 内置文件处理模块,

node.js 爬虫以及数据导出到excel

//server.js 1 var http = require('http'), 2 url = require("url"), 3 superagent = require("superagent"), 4 cheerio = require("cheerio"), 5 async = require("async"), 6 eventproxy = require('eventproxy'), 7 fs = requir

Node.js 爬虫初探

前言 在学习慕课网视频和Cnode新手入门接触到爬虫,说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherrio. 使用http直接获取url路径对应网页资源,然后使用cherrio分析. 这里我主要是把慕课网教学视频提供的案例自己敲了一边,加深理解.在coding的过程中,我第一次把jq获取后的对象直接用forEach遍历,直接报错,是因为jq没有对应的这个方法,只有js数组可以调用. 知识点 ①:superagent抓去网页工具.我暂时

Node.js 爬虫批量下载美剧 from 人人影视 HR-HDTV

这两天发现了一个叫看知乎的网站,是知乎大牛苏莉安做的,其中爬虫使用的 Node.js.这里就针对上一篇博客中的美剧小爬虫,改用 nodejs 进行实现一下,体验一下强大的 Node.js. 如果之前没有用过 JavaScript,不妨到 http://www.codecademy.com/  做一下 JavaScript 和 jQuery 的入门练习,快速熟悉一下基本语法,有其他语言基础一天时间足够.有基本的了解后,就会发现 JavaScript 的两大特点: 使用基于原型(prototype)

Node.js 爬虫,自动化抓取文章标题和正文

持续进行中... 目标: 动态User-Agent模拟浏览器 √ 支持Proxy设置,避免被服务器端拒绝 √ 支持多核模式,发挥多核CPU性能 √ 支持核内并发模式 √ 自动解码非英文站点,避免乱码出现 √ 自动队列管理,持续化PUSH √ 自动抓取文章标题和正文 √ 性能优化 测试用例,自动化测试 我的测试环境: 9 台安装 ElasticSearch 1.3.2 的 centos,48G内存,8核 3 台 Node.js v0.11.14 的 centos,16G内存,8核,用作爬虫 3 台

使用node.js的bodyParser中间件读取post数据解析

昨天我们使用的网关转发数据时出了点问题! 情景是这样的,另一方以Post的形式向我的node.js服务推送JSON数据.但是使用bodyParser中间件后,在req.body中拿不到任何信息. 代码如下: app.use(bodyParser.json()); app.post('/api/*',function(req,res){ _option.onMessage({url:req.url,query:req.query,body:req.body},function(error,data

node.js ejs模板引擎将后端数据赋值给前端js

使用node.js的Express脚手架生成项目默认是jade模板引擎,jade引擎实在是太难用了,这么难用还敢设为默认的模板引擎,过分了啊!用handlebars模板引擎写还说的过去,但笔者更愿意使用ejs,选它是因为跟Asp.Net的模板引擎有点相似吧. 先来看一下这几个模板引擎: jade模板 html head title #{title} meta(charset="UTF-8") body div.description #{description} ul - each d