最近正在学习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. 线程池之 ThreadPoolExecutor

    线程池之 ThreadPoolExecutor + 面试题 线程池介绍 线程池(Thread Pool):把一个或多个线程通过统一的方式进行调度和重复使用的技术,避免了因为线程过多而带来使用上的开销. ...

  2. koa中间执行机制

    start 基于 koa 2.11 按以下流程分析: const Koa = require('koa'); const app = new Koa(); const one = (ctx, next ...

  3. shell命令之一天一见:grep

    一. 简介 grep (缩写来自Globally search a Regular Expression and Print)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来 ...

  4. 2020你还不会Java8新特性?

    Java8(1)新特性介绍及Lambda表达式 前言: 跟大娃一块看,把原来的电脑拿出来放中间看视频用 --- 以后会有的课程 难度 深入Java 8 难度1 并发与netty 难度3 JVM 难度4 ...

  5. Luogu P1330 封锁阳光大学 (黑白染色)

    题意: 无向图,给一个顶点染色可以让他相邻的路不能通过,但是相邻顶点不能染色,求是否可以让所有的路不通,如果可以求最小染色数. 思路: 对于无向图中的每一个连通子图,都只有两种染色方法,或者染不了,直 ...

  6. Codeforces 977D Divide by three, multiply by two(拓扑排序)

      Polycarp likes to play with numbers. He takes some integer number xx, writes it down on the board, ...

  7. Flyway 的使用及Spring Boot集成Flyway

    一.简单介绍 Flyway 是一个开源.跨环境的数据库迁移工具,它强烈主张简单性和约定性而不是配置. Flyway 是一个便于多人开发对数据库管理的工具,将sql语句写入文件中,只需要在控制台输入指令 ...

  8. kendo ui - core

    通过CDN 引入kendo-ui-core git地址:http://www.telerik.com/kendo-ui<link href="http://kendo.cdn.tele ...

  9. AppBox实战: 如何实现一对多表单的增删改查

      本篇通过完整示例介绍如何实现一对多关系表单的相应服务及视图. 一.准备数据结构   示例所采用的数据结构为"物资需求"一对多"物资清单",通过IDE的实体设 ...

  10. Nginx总结(八)Nginx服务器的日志管理及配置

    前面讲了如何配置Nginx虚拟主机,大家可以去这里看看nginx系列文章:https://www.cnblogs.com/zhangweizhong/category/1529997.html 今天要 ...