node crawler如何添加promise支持


Posted in Javascript onFebruary 01, 2020

背景

最近在组内做一些爬虫相关的工作,本来想自己简单造个轮子的,但是经网友推荐后,采用了node-crawler,用了一段时间过后,确实满足了我的绝大部分需求,但是其 api 却不支持 promise,而且我还需要一些同步爬取、同步处理的能力,如果不用 promise 的话,写法很不优雅,所以我就简单地给其封装了一层 promise api

现状

目前 node-crawler 的使用方式不支持 promise ,这里直接给出 npm 上的使用例子

const Crawler = require("crawler")

// 实例化
const c = new Crawler({
  // ... 可以传入一些配置
  callback : function (error, res, done) {
    // 请求回调,实例化的时候传入的 callback 是作为默认 callback 的,后续每次抓取如果没有传入 callback,那么都会调用默认 callback
    done();
  }
})

// 爬取
c.queue([{
  uri: 'http://parishackers.org/',
  jQuery: false,
 
  // The global callback won't be called
  callback: function (error, res, done) {
    if(error){
      console.log(error);
    }else{
      console.log('Grabbed', res.body.length, 'bytes');
    }
    done();
  }
}])

这样的回调方式对于多爬虫同步爬取很不友好

改造

理想使用方式:

const Crawler = require('crawler')

const c = new Crawler({
  // 一些默认配置
})

c
.queue({
  uri: 'xxx'
})
.then(res => {
  // 抓取成功
})
.catch(err => {
  // 抓取失败
})

改造方案:

// utils/crawler.js
const Crawler = require('crawler')
const defaultOptions = {
 jQuery: false,
 rateLimit: fetchRateLimit,
 retries: 0,
 timeout: fetchTimeout,
}

module.exports = class PromiseifyCrawler extends Crawler {
  // namespace 是为了后续抓取结果统一上报时候进行区分
  constructor(namespace = 'unknow', options = {}) {
   if (typeof namespace === 'object') {
    options = namespace
    namespace = 'unknow'
   }
   
   options = merge({}, defaultOptions, options)

   const cb = options.callback
   options.callback = (err, res, done) => {
    typeof cb === 'function' && cb(err, res, noop)
    process.nextTick(done)
    // 在这里可以自定义抓取成功还是失败
    // 我这里直接设置的是如果 http code 不是 200 就视为错误
    // 而且在这里也可以做一些抓取成功失败的统计
    if (err || res.statusCode !== 200) {
     if (!err) err = new Error(`${res.statusCode}-${res.statusMessage}`)
     err.options = res.options
     err.options.npolisReject(err)
    } else {
     res.options.npolisResolve(res)
    }
   }
   options.headers = Object.assign({}, options.headers, {
    'X-Requested-With': 'XMLHttpRequest',
   })
   super(options)
  }
 
  queue(options = {}) {
   // 每次抓取都是一个新的 promise
   return new Promise((resolve, reject) => {
    // 然后在 options 里挂载上 resolve 和 reject
    // 这样在全局 callback 上就可以用到了
    options.npolisResolve = resolve
    options.npolisReject = reject

    const pr = options.preRequest
    options.preRequest = (options, done) => {
     typeof pr === 'function' && pr(options, noop)
     // 在这里也可以做一些通用的抓取前的处理
     
     done()
    }

    super.queue(options)
   })
  }
  
  // direct api 同理
 }
// 使用
const Crawler = require('./utils/crawler')

const crawler = new Crawler('示例爬虫 namespace')

crawler
.queue({
 uri: 'xxx',
 preRequest: options => log('开始抓取'),
})
.then(res => {
 log('爬取成功')
 return res
})
.catch(err => {
 log('爬取失败')
 throw err
})
promise 化后,多个爬取任务同步爬取写法就友好多了:

// 抓取任务1
const fetchTask1 = () => crawler.queue({/* 配置 */}).then(res => handle(res))
// 抓取任务2
const fetchTask2 = () => crawler.queue({/* 配置 */}).then(res => handle(res))

const fetch = () => {
  return Promise.all([
    fetchTask1(),
    fetchTask2(),
  ])
}

fetch()

这样就完成了对 node-crawler 的 promise 化改造了

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
ECMAScript 5严格模式(Strict Mode)介绍
Mar 02 Javascript
原生的强大DOM选择器querySelector介绍
Dec 21 Javascript
Extjs让combobox写起来简洁又漂亮
Jan 05 Javascript
Javascript设计模式之装饰者模式详解篇
Jan 17 Javascript
js实现图片旋转 js滚动鼠标中间对图片放大缩小
Jul 05 Javascript
input框中自动展示当前日期yyyy/mm/dd的实现方法
Jul 06 Javascript
基于Vue中点击组件外关闭组件的实现方法
Mar 06 Javascript
IE9 elementUI文件上传的问题解决
Oct 17 Javascript
JS使用H5实现图片预览功能
Sep 30 Javascript
如何在Node和浏览器控制台中打印彩色文字
Jan 09 Javascript
手把手教你实现 Promise的使用方法
Sep 02 Javascript
vue 页面跳转的实现方式
Jan 12 Vue.js
js 计算月/周的第一天和最后一天代码
Feb 01 #Javascript
js获取本日、本周、本月的时间代码
Feb 01 #Javascript
js 获取本周、上周、本月、上月、本季度、上季度的开始结束日期
Feb 01 #Javascript
vue学习笔记之过滤器的基本使用方法实例分析
Feb 01 #Javascript
vue学习笔记之slot插槽基本用法实例分析
Feb 01 #Javascript
vue学习笔记之作用域插槽实例分析
Feb 01 #Javascript
vue子组件改变父组件传递的prop值通过sync实现数据双向绑定(DEMO)
Feb 01 #Javascript
You might like
PHPMailer邮件类利用smtp.163.com发送邮件方法
2008/09/11 PHP
PHP 加密与解密的斗争
2009/04/17 PHP
通过php快速统计某个数据库中每张表的数据量
2012/09/04 PHP
thinkphp在php7环境下提示Cannot use ‘String’ as class name as it is reserved的解决方法
2016/09/30 PHP
快速保存网页中所有图片的方法
2006/06/23 Javascript
js判断undefined变量类型使用typeof
2013/06/03 Javascript
js代码实现随机颜色的小方块
2015/07/30 Javascript
全面解析JavaScript中的valueOf与toString方法(推荐)
2016/06/14 Javascript
详解Angular2中Input和Output用法及示例
2017/05/21 Javascript
Angularjs实现上传图片预览功能
2017/09/01 Javascript
完美解决手机网页中输入框被输入法遮挡的问题
2017/12/19 Javascript
Angular实现的自定义模糊查询、排序及三角箭头标注功能示例
2017/12/28 Javascript
vue项目中使用ueditor的实例讲解
2018/03/05 Javascript
解决Vue 通过下表修改数组,页面不渲染的问题
2018/03/08 Javascript
vue中使用gojs/jointjs的示例代码
2018/08/24 Javascript
jsonp跨域及实现百度首页联想功能的方法
2018/08/30 Javascript
Node.js原生api搭建web服务器的方法步骤
2019/02/15 Javascript
VUE table表格动态添加一列数据,新增的这些数据不可以编辑(v-model绑定的数据不能实时更新)
2020/04/03 Javascript
vue路由权限校验功能的实现代码
2020/06/07 Javascript
Python实现的简单万年历例子分享
2014/04/25 Python
Python httplib模块使用实例
2015/04/11 Python
Python编码爬坑指南(必看)
2016/06/10 Python
Python科学计算之NumPy入门教程
2017/01/15 Python
Python输出\u编码将其转换成中文的实例
2018/12/15 Python
Python Image模块基本图像处理操作小结
2019/04/13 Python
Python安装selenium包详细过程
2019/07/23 Python
在django模板中实现超链接配置
2019/08/21 Python
基于python实现雪花算法过程详解
2019/11/16 Python
Tensorflow tf.nn.depthwise_conv2d如何实现深度卷积的
2020/04/20 Python
解决python虚拟环境切换无效的问题
2020/04/30 Python
python打开文件的方式有哪些
2020/06/29 Python
使用HTML5 Canvas API绘制弧线的教程
2016/03/22 HTML / CSS
伦敦哈德森鞋:Hudson Shoes
2018/02/06 全球购物
离婚协议书范文
2015/01/26 职场文书
浅谈Python类的单继承相关知识
2021/05/12 Python
MySQL为id选择合适的数据类型
2021/06/07 MySQL