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 相关文章推荐
javascript 简练的几个函数
Aug 29 Javascript
JQuery教学之性能优化
May 14 Javascript
jQuery回调方法使用示例
Jun 26 jQuery
原生JS实现的双色球功能示例
Feb 02 Javascript
JS 实现分页打印功能
May 16 Javascript
JavaScript实现的简单Tab点击切换功能示例
Jul 06 Javascript
在node中使用jwt签发与验证token的方法
Apr 03 Javascript
js中arguments对象的深入理解
May 14 Javascript
浅谈小程序globalData的那些事儿
Nov 01 Javascript
JavaScript的console命令使用实例
Dec 03 Javascript
小程序卡片切换效果组件wxCardSwiper的实现
Feb 13 Javascript
原生JS实现无缝轮播图片
Jun 24 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的session过期设置
2013/06/29 PHP
php中的静态变量的基本用法
2014/03/20 PHP
详解 PHP加密解密字符串函数附源码下载
2015/12/18 PHP
PHP批量去除BOM头内容信息代码
2016/03/11 PHP
购物车实现的几种方式优缺点对比
2018/05/02 PHP
PHP实现单例模式建立数据库连接的方法分析
2020/02/11 PHP
ExtJs3.0中Store添加 baseParams 的Bug
2010/03/10 Javascript
JQuery中的ready函数冲突的解决方法
2010/05/17 Javascript
js简单实现表单中点击按钮动态增加输入框数量的方法
2015/08/18 Javascript
jquery特效 点击展示与隐藏全文
2015/12/09 Javascript
JS实现的表头列头固定页面功能示例
2017/01/10 Javascript
jQuery源码解读之extend()与工具方法、实例方法详解
2017/03/30 jQuery
基于vue的换肤功能的示例代码
2017/10/10 Javascript
解决vue页面刷新或者后退参数丢失的问题
2018/03/13 Javascript
详解微信小程序-扫一扫 wx.scanCode() 扫码大变身
2019/04/30 Javascript
在Vue项目中使用Typescript的实现
2019/12/19 Javascript
[00:28]DOTA2北京网鱼队选拔赛
2015/04/08 DOTA
scrapy自定义pipeline类实现将采集数据保存到mongodb的方法
2015/04/16 Python
老生常谈Python序列化和反序列化
2017/06/28 Python
python3.6 +tkinter GUI编程 实现界面化的文本处理工具(推荐)
2017/12/20 Python
python3 破解 geetest(极验)的滑块验证码功能
2018/02/24 Python
利用python实现AR教程
2019/11/20 Python
Pytorch实现LSTM和GRU示例
2020/01/14 Python
HTML5边玩边学(1)画布实现方法
2010/09/21 HTML / CSS
Cotton On美国网站:澳洲时装连锁品牌
2016/10/25 全球购物
MVMT手表官方网站:时尚又实惠的高品质手表
2016/12/04 全球购物
Ralph Lauren拉夫·劳伦美国官网:带有浓郁美国气息的高品味时装品牌
2017/11/01 全球购物
如何利用XMLHTTP检测URL及探测服务器信息
2013/11/10 面试题
2014年党员自我剖析材料
2014/10/07 职场文书
单位租房协议书范本
2014/12/04 职场文书
医药公司采购员岗位职责
2015/04/03 职场文书
2015年村党支部工作总结
2015/04/30 职场文书
2015年党小组工作总结
2015/05/26 职场文书
2016高考寄语集锦
2015/12/04 职场文书
多属性、多分类MySQL模式设计
2021/04/05 MySQL
JavaScript实现贪吃蛇游戏
2021/06/16 Javascript