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 相关文章推荐
FireFox下XML对象转化成字符串的解决方法
Dec 09 Javascript
解析javascript 数组以及json元素的添加删除
Jun 26 Javascript
优化javascript的执行效率一些方法总结
Dec 25 Javascript
为jquery的ajaxfileupload增加附加参数的方法
Mar 04 Javascript
javascript实现判断鼠标的状态
Jul 10 Javascript
基于jquery实现在线选座订座之影院篇
Aug 24 Javascript
jQuery获取某天的农历日期并判断是否除夕或新年的方法
Mar 01 Javascript
基于Jquery插件Uploadify实现实时显示进度条上传图片
Mar 26 Javascript
浅谈JS中的三种字符串连接方式及其性能比较
Sep 02 Javascript
JavaScript数组,JSON对象实现动态添加、修改、删除功能示例
May 26 Javascript
VUE-Table上绑定Input通过render实现双向绑定数据的示例
Aug 27 Javascript
Bootstrap table 实现树形表格联动选中联动取消功能
Sep 30 Javascript
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
PHP中使用foreach和引用导致程序BUG的问题介绍
2012/09/05 PHP
php 目录遍历、删除 函数的使用介绍
2013/04/28 PHP
JavaScript 创建对象和构造类实现代码
2009/07/30 Javascript
js null,undefined,字符串小结
2010/08/21 Javascript
jQuery lazyload 的重复加载错误以及修复方法
2010/11/19 Javascript
js/jquery判断浏览器的方法小结
2014/09/02 Javascript
angularJS中$apply()方法详解
2015/01/07 Javascript
JavaScript 事件绑定及深入
2015/04/13 Javascript
jquery+html5时钟特效代码分享(可设置闹钟并且语音提醒)
2020/03/30 Javascript
js随机生成字母数字组合的字符串 随机动画数字
2015/09/02 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
2015/11/09 Javascript
javascript简单实现等比例缩小图片的方法
2016/07/27 Javascript
AngularJS实现给动态生成的元素绑定事件的方法
2016/12/14 Javascript
通过BootStrap-select插件 js jQuery控制select属性变化
2017/01/03 Javascript
RequireJS 依赖关系的实例(推荐)
2017/01/21 Javascript
Vue.js学习笔记之修饰符详解
2017/07/25 Javascript
Vue实现textarea固定输入行数与添加下划线样式的思路详解
2018/06/28 Javascript
超详细的5个Shell脚本实例分享(值得收藏)
2019/08/15 Javascript
原生js实现的观察者和订阅者模式简单示例
2020/04/18 Javascript
[01:08:09]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第二局
2016/03/02 DOTA
python实现猜单词小游戏
2020/05/22 Python
对python中的高效迭代器函数详解
2018/10/18 Python
Python PIL读取的图像发生自动旋转的实现方法
2019/07/05 Python
使用python代码进行身份证号校验的实现示例
2019/11/21 Python
Python如何访问字符串中的值
2020/02/09 Python
python如何从键盘获取输入实例
2020/06/18 Python
windows支持哪个版本的python
2020/07/03 Python
HTML5 画布canvas使用方法
2016/03/18 HTML / CSS
植物选择:Botanic Choice
2017/02/15 全球购物
New Era英国官网:美国棒球帽品牌
2018/03/21 全球购物
德国富尔达运动鞋店:43einhalb
2020/12/25 全球购物
高中军训感想300字
2014/03/04 职场文书
老公保证书
2015/01/17 职场文书
只需要12页,掌握撰写一流商业计划书的技巧
2019/05/07 职场文书
公司开业的祝贺语大全(60条)
2019/07/05 职场文书
排查并解决MySQL生产库内存使用率高的报警
2022/04/11 MySQL