node爬虫爬取csdn数据

418 阅读3分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

环境搭建

必须安装Node,我装的是8.11.2版本,Mac开发;

使用到的一些第三方库:

  • 后端服务: express
  • 发出http请求: superagent
  • 控制并发请求:async + eventproxy
  • 分析网页内容:cheerio

直接配置一下package.json:

{
  "name": "spider",
  "version": "0.0.0",
  "description": "learn nodejs on github",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "async": "^2.0.0-rc.6",
    "cheerio": "^0.20.0",
    "eventproxy": "^0.3.4",
    "express": "^4.9.5",
    "superagent": "^2.3.0"
  }
}

配置好后 nom install 安装所需依赖;

接下来开始写爬虫。

后台服务部分

实现的功能是接收前端请求启动爬虫,完成信息爬取之后将信息返回给前端。后台服务部分我这里使用了 express 框架,这里比较简单也可以使用原生的 http 模块。简单框架如下:

var express = require('express');
var app = express();
app.get('/', function (req, res, next) {
    // your code here
});
app.listen(3000, function (req, res) {
    console.log('app is running at port 3000');
});

在 get 处理中插入我们的响应代码,包括启动爬虫,结果信息输出等。

文章链接的爬取

superagent.get(Url).end(function (err, res) {
    if (err) { return next(err); }
    // your code here
});

Url 为我们请求的地址,使用 get 的方式请求,其实效果跟你用浏览器打开 Url 的效果是一样的,返回来的数据都放在 res 中,对 res 分析就可以得到我们想要的数据了。

数据的处理

这里我们用到的是 cheerio 这个库,他可以让我们以 jQuery 的方式操作返回的数据,实在太贴心了。

注:语法大体跟jquery一致,API可查看:cnodejs.org/topic/5203a…

// 提取作者博客链接,注意去重
            var $ = cheerio.load(sres.text);
            $('.blog_list').each(function (i, e) {
                var u = $('.nickname', e).attr('href');
                if (authorUrls.indexOf(u) === -1) {
                    authorUrls.push(u);
                }
            });

文章作者信息的爬取

superagent.get(myurl)
                .end(function (err, ssres) {
                    if (err) {
                        callback(err, myurl + ' error happened!');
                    }
                    var $ = cheerio.load(ssres.text);
                    var result = {
                        userId: url.parse(myurl).pathname.substring(1),
                        userName: $(".name #uid").text(),
                        blogTitle: $(".title-blog a").text(),
                        visitCount: $('.grade-box dl').eq(1).children('dd').attr("title"),
                        score: $('.grade-box dl').eq(2).children('dd').attr("title"),
                        /* oriCount: parseInt($('#blog_statistics>li').eq(0).text().split(/[::]/)[1]),
                        copyCount: parseInt($('#blog_statistics>li').eq(1).text().split(/[::]/)[1]),
                        trsCount: parseInt($('#blog_statistics>li').eq(2).text().split(/[::]/)[1]), */
                        cmtCount: parseInt($('#blog_statistics>li').eq(3).text().split(/[::]/)[1])
                    };
                    callback(null, result);
                });

这里可以根据自己需要,自行筛选数据。

并发的控制

因为我们的请求都是异步的,所以需要执行成功的回调函数中执行下一步操作,在多并发的情况下,就需要一个计数器来判断是否所有并发均已成功执行完。这里用到的是 eventproxy 这个库来替我们管理并发结果。

我们爬取CSDN上web前端前3页,所以我们要执行 3 次爬取文章链接,用 eventproxy 的写法就是:

var baseUrl = 'http://blog.csdn.net/web/index.html';
var pageUrls = [];
for (var _i = 1; _i < 4; _i++) {
    pageUrls.push(baseUrl + '?&page=' + _i);
}
ep.after('get_topic_html', pageUrls.length, function (eps) {
    // 文章链接都已经爬取完了
});
pageUrls.forEach(function (page) {
    superagent.get(page).end(function (err, sres) {
        // 文章链接的爬取
        ep.emit('get_topic_html', 'get authorUrls successful');
    });
});

简单来说,就是会检测 'get_topic_html' 的emit事件,发生指定次数之后就调用 ep.after 函数。

并发请求数控制

本来呢,到这里就完了,但是我们爬取作者信息时都是用异步操作的,所以同时可能会有几十甚至几百个请求同时发送给目标网站。出于安全角度考虑,目标网站可能会拒绝我们的请求,所以我们要控制并发数量,这里我们用的是 async这个库来实现。

这里 authorUrls 是我们前一步爬取好的作者链接数组,async 会根据数组长度依次执行。之前在作者信息爬取部分我们使用了回调函数返回数据,这个也是async 提供的接口。最终数组中所有元素都被执行了一遍之后,就会将 callback 返回的数据放入 result 数组中,将这个数组返回给前端即可。

效果

通过 node app.js 执行后台程序,在 postman 中输入 http://localhost:3000 查看结果:

如上,就算完成了,还可以用它去爬取别的想爬的数据,