最近正在学习node.js,就像搞一些东西来玩玩,于是这个简单的爬虫就诞生了。

准备工作

  1. node.js爬虫肯定要先安装node.js环境
  2. 创建一个文件夹
  3. 在该文件夹打开命令行,执行npm init初始化项目

正式开始

安装依赖

  • express 用来搭建一个简单http服务器,也可以使用node原生api
  • cheerio 相当于node版的jQuery,用来解析页面
  • superagent 用来请求目标页面
  • eventproxy 解决同时处理多个页面的问题

直接使用npm install express cheerio superagent eventproxy 来安装依赖包,当然你也可以用别的方法。

创建建好目录

node-spider-csdn
├─ .gitignore
├─ node_modules
├─ README.md
├─ index.js 项目入口
├─ package-lock.json
├─ package.json
└─ routes
└─ csdn.js 爬虫主要代码

创建一个Http服务器

index.js文件中,实例化一个express对象,启动一个Http服务

const express = require('express');

const app = express();

app.listen(3000, function() {
console.log('running in http://127.0.0.1:3000');
});

这样就启动了一个简单的Http本地服务,执行node index.js后通过http://127.0.0.1:3000就可以访问到这个服务器。有关Express的更多内容可以参考官方文档

编写csdn.js模块

先引入csdn.js文件并且添加路由

const express = require('express');
const csdn = require('./routes/csdn.js'); const app = express(); app.use(csdn); app.listen(3000, function() {
console.log('running in http://127.0.0.1:3000');
});

然后开始编写csdn.js

整体结构

// 引入需要的第三方包
const cheerio = require('cheerio');
const superagent = require('superagent');
const express = require('express');
const eventproxy = require('eventproxy'); const router = express.Router(); // 挂载路由
const ep = new eventproxy(); router.get('/csdn/:name', function(req, res) {
const name = req.params.name; // 用户id
// 具体实现...
}); // 将router暴露出去
module.exports = router;

分析页面

整体结构写好后就要开始分析CSDN用户文章页面的HTML了。

随便找一个人的博客,经过观察发现:

  • 原创文章的完整url:https://blog.csdn.net/l1028386804/article/list/2?t=1
  • CSDN的文章列表是40篇一页
  • 分页控件是动态生成的,所以无法直接通过HTML解析获得

然后我们通过开发者工具查看文章列表结构,可以发现:

  • 文章信息都在类名为article-item-box的盒子中
  • id信息在该盒子的data-articleid属性中

还有一些其他的信息都很容易能查到,比如博主原创文章总数值等,可以在以后需要的时候再过来查看。

获取所有文章页面

因为无法直接获得分页信息,所以我们通过文章总数 / 每页文章数来获取所有的页面。

首先获取文章的总数:

/**
* 获取总文章数目
* @param {String} url 页面路径
* @param {Function} callback 回调
*/
let getArticleNum = function (url, callback) {
superagent.get(url).end(function (err, html) {
if (err) {
console.log(`err = ${err}`);
}
let $ = cheerio.load(html.text);
let num = parseInt($('.data-info dl').first().attr('title')); callback(num);
});
};

然后利用简单的循环获取所有文章页面:

// ...
router.get('/csdn/:name', function(req, res) {
const name = req.params.name;
getArticleNum(`https://blog.csdn.net/${name}`, function (num) {
let pages = []; // 保存要抓取的页面 let pageNum = Math.ceil(num / 40); // 计算一共有多少页面 for (let i = 1; i <= pageNum; i++) {
pages.push(`https://blog.csdn.net/${name}/article/list/${i}?t=1`);
}
// ...
});
});
// ...

我们可以通过console.log()或者res.send()来查看获取的网址是否正确

遍历获取所有页面的HTML

// ...
router.get('/csdn/:name', function (req, res) {
const name = req.params.name; getArticleNum(`https://blog.csdn.net/${name}`, function (num) {
let pages = [];
let articleData = []; // 保存所有文章数据 let pageNum = Math.ceil(num / 40); // 计算一共有多少页面 for (let i = 1; i <= pageNum; i++) {
pages.push(`https://blog.csdn.net/${name}/article/list/${i}?t=1`);
} // 获取所有页面的文章信息
pages.forEach(function (targetUrl) {
superagent.get(targetUrl).end(function (err, html) {
if (err) {
console.log(`err ${err}`);
}
let $ = cheerio.load(html.text); // 当前页面的文章列表
let articlesHtml = $('.article-list .article-item-box'); // 遍历当前页的文章列表
for (let i = 0; i < articlesHtml.length; i++) {
// 解析获取文章信息
// push到articleData中
// ...
}
});
});
});
});
// ...

解析文章信息

因为获取到的有些文本中空格太多,所以需要用到正则表达式来去除多余的空格。

cheerio对于Document的操作和jQuery基本一样,所以有前端基础的可以很轻松上手。

/**
* 解析html字符串,获取文章信息
* @param {String} html 包含文章信息的html
* @param {Number} index 文章索引
*/
let analysisHtml = function (html, index) {
return {
id: html.eq(index).attr('data-articleid'),
title: html.eq(index).find('h4 a').text().replace(/\s+/g, '').slice(2),
link: html.eq(index).find('a').attr('href'),
abstract: html.eq(index).find('.content a').text().replace(/\s+/g, ''),
shared_time: html.eq(index).find('.info-box .date').text().replace(/\s+/, ''),
read_count: html.eq(index).find('.info-box .read-num .num').first().text().replace(/\s+/, ''),
comment_count: html.eq(index).find('.info-box .read-num .num').last().text().replace(/\s+/, '')
};
};
// ...
// 遍历当前页的文章列表
for (let i = 0; i < articlesHtml.length; i++) {
let article = analysisHtml(articlesHtml, i);
articleData.push(article);
// ...
}
// ...

我们已经获取到所有文章的信息数据,但是因为获取各个页面的文章时是并发异步进行的,所以要同时利用这些数据特殊的方法。

处理并发异步操作

这里我使用的是“计数器”eventproxy,还有很多其他的方法都可以解决这个问题。

// ...
pages.forEach(function (targetUrl) {
superagent.get(targetUrl).end(function (err, html) {
if (err) {
console.log(`err ${err}`);
}
let $ = cheerio.load(html.text); let articlesHtml = $('.article-list .article-item-box'); for (let i = 0; i < articlesHtml.length; i++) {
let article = analysisHtml(articlesHtml, i);
articleData.push(article); ep.emit('blogArtc', article); // 计数器
}
});
}); // 当所有'blogArtc'完成后,触发回调
ep.after('blogArtc', num, function (data) {
res.json({
status_code: 0,
data: data
});
});
// ...

这样,一个简单的node爬虫就写好了,执行node index.js启动服务后,在浏览器中输入http://127.0.0.1:3000/csdn/xxxx就可以获得xxxx(这是id)的全部文章了。

完整代码

参考文章

nodejs爬虫--抓取CSDN某用户全部文章的更多相关文章

  1. NodeJs爬虫抓取古代典籍,共计16000个页面心得体会总结及项目分享

    项目技术细节 项目大量用到了 ES7 的async 函数, 更直观的反应程序了的流程.为了方便,在对数据遍历的过程中直接使用了著名的async这个库,所以不可避免的还是用到了回调promise ,因为 ...

  2. Python实现抓取CSDN博客首页文章列表

    1.使用工具: Python3.5 BeautifulSoup 2.抓取网站: csdn首页文章列表 http://blog.csdn.net/ 3.分析网站文章列表代码: 4.实现抓取代码: __a ...

  3. Python分布式爬虫抓取知乎用户信息并进行数据分析

    在以前的文章中,我写过一篇使用selenium来模拟登录知乎的文章,然后在很长一段时间里都没有然后了... 不过在最近,我突然觉得,既然已经模拟登录到了知乎了,为什么不继续玩玩呢?所以就创了一个项目, ...

  4. Python爬虫抓取csdn博客

    昨天晚上为了下载保存某位csdn大牛的所有博文,写了一个爬虫来自己主动抓取文章并保存到txt文本,当然也能够 保存到html网页中. 这样就能够不用Ctrl+C 和Ctrl+V了,很方便.抓取别的站点 ...

  5. Nodejs实现爬虫抓取数据

    开始之前请先确保自己安装了Node.js环境,还没有安装的的童鞋请自行百度安装教程...... 1.在项目文件夹安装两个必须的依赖包 npm install superagent --save-dev ...

  6. 爬虫抓取页面数据原理(php爬虫框架有很多 )

    爬虫抓取页面数据原理(php爬虫框架有很多 ) 一.总结 1.php爬虫框架有很多,包括很多傻瓜式的软件 2.照以前写过java爬虫的例子来看,真的非常简单,就是一个获取网页数据的类或者方法(这里的话 ...

  7. python 爬虫抓取心得

    quanwei9958 转自 python 爬虫抓取心得分享 urllib.quote('要编码的字符串') 如果你要在url请求里面放入中文,对相应的中文进行编码的话,可以用: urllib.quo ...

  8. Java 实现 HttpClients+jsoup,Jsoup,htmlunit,Headless Chrome 爬虫抓取数据

    最近整理一下手头上搞过的一些爬虫,有HttpClients+jsoup,Jsoup,htmlunit,HeadlessChrome 一,HttpClients+jsoup,这是第一代比较low,很快就 ...

  9. 如何利用Python网络爬虫抓取微信好友数量以及微信好友的男女比例

    前几天给大家分享了利用Python网络爬虫抓取微信朋友圈的动态(上)和利用Python网络爬虫爬取微信朋友圈动态——附代码(下),并且对抓取到的数据进行了Python词云和wordart可视化,感兴趣 ...

随机推荐

  1. Nginx核心模块

    error_log 语法:error_log file [ debug | info | notice | warn | error | crit ]默认值:${prefix}/logs/error. ...

  2. SpringBoot缓存篇Ⅰ--- 缓存抽象

    缓存是每一个系统应该考虑的功能,它可以用来加速系统的访问,提升系统性能,例如要经常访问的高频热点数据,例如某一个商品网站的商品信息,商品信息存储在数据库中,若每次访问都要查询数据库的话,这样的操作耗时 ...

  3. 程序的健壮性Robustness

    所谓的程序健壮性是指处理异常的能力,在异常中能够独立处理异常,并且把正确的答案输出. 例如: 有一个程序能够下载一个文件到指定的路径,但是这个路径是不存在的,因此程序必须要处理这个情况. 例1:下面的 ...

  4. AcWing 785.快速排序

    AcWing 785.快速排序题解 题目描述 给定你一个长度为n的整数数列. 请你使用快速排序对这个数列按照从小到大进行排序. 并将排好序的数列按顺序输出. 输入格式 输入共两行,第一行包含整数 n. ...

  5. C语言系列之实验楼笔记(一)

    创建C程序的几个过程: 1.编辑:创建和修改C程序的源代码 2.编译:编译器可以将源代码转成机器语言.linux 这些文件扩展名.o 3.链接:通过一次完成编译和链接 4.执行;运行程序 打开xfce ...

  6. Windows玩转Kubernetes系列3-Centos安装K8S

    以往文章参考: Windows玩转Kubernetes系列1-VirtualBox安装Centos Windows玩转Kubernetes系列2-Centos安装Docker 安装K8S yum in ...

  7. ORACLE ITL事务槽

    讲到ITL(事务槽)必定先说数据块,那么什么是数据块呢?先给大家上一个数据块结构图 数据块分别由块头.ITL(事务槽).表信息区.行信息区.块空闲区.行数据区组成,其中ITL用来记录在数据块发生的所有 ...

  8. 磁盘文件系统管理与LVM逻辑卷

    一.磁盘以及分区管理 无论是Linux系统还是Windows系统.当现有硬盘的规划不能满足当前需求时.我们就需要将其重新规划和调整 实现上述操作我们就需要用到fdisk磁盘及分区管理工具.此工具是大多 ...

  9. python学习(6)选择排序算法简单代码

    选择排序的基本思想是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录.基于此思想的算法主要有简单选择排序.树型选择排序和堆排序.[1] 简单选择排序的基 ...

  10. 【MySQL 线上 BUG 分析】之 多表同字段异常:Column ‘xxx’ in field list is ambiguous

    一.生产出错! 今天早上11点左右,我在工作休息之余,撸了一下猫.突然,工作群响了,老大在里面说:APP出错了! 妈啊,这太吓人了,因为只是说了出错,但是没说错误的信息.所以我赶紧到APP上看看. 这 ...