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 相关文章推荐
document 和 document.all 分别什么时候用
Jun 22 Javascript
JavaScript 动态改变图片大小
Jun 11 Javascript
jquery命令汇总,方便使用jquery的朋友
Jun 26 Javascript
JS滚轮事件onmousewheel使用介绍
Nov 01 Javascript
angularjs 源码解析之scope
Aug 22 Javascript
js实现带缓动动画的导航栏效果
Jan 16 Javascript
vue2.0 如何把子组件的数据传给父组件(推荐)
Jan 15 Javascript
node基于puppeteer模拟登录抓取页面的实现
May 09 Javascript
Vue登录注册并保持登录状态的方法
Aug 17 Javascript
vue src动态加载请求获取图片的方法
Oct 17 Javascript
微信小程序wx.request拦截器使用详解
Jul 09 Javascript
Vue移动端实现图片上传及超过1M压缩上传
Dec 23 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
拼音码表的生成
2006/10/09 PHP
php利用header函数实现文件下载时直接提示保存
2009/11/12 PHP
Javascript实现CheckBox的全选与取消全选的代码
2010/07/20 Javascript
js对象数组按属性快速排序
2011/01/31 Javascript
JavaScript判断一个URL链接是否有效的实现方法
2011/10/08 Javascript
如何使用jquery动态加载js,css文件实现代码
2013/04/03 Javascript
jquery解析JSON数据示例代码
2014/03/17 Javascript
基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用
2015/12/16 Javascript
jQuery的Each比JS原生for循环性能慢很多的原因
2016/07/05 Javascript
JavaScript生成.xls文件的代码
2016/12/22 Javascript
react-native 封装选择弹出框示例(试用ios&android)
2017/07/11 Javascript
JS实现的加减乘除四则运算计算器示例
2017/08/09 Javascript
vue-router 组件复用问题详解
2018/01/22 Javascript
Python自定义函数的创建、调用和函数的参数详解
2014/03/11 Python
Python中线程编程之threading模块的使用详解
2015/06/23 Python
使用Python操作excel文件的实例代码
2017/10/15 Python
python判断输入日期为第几天的实例
2018/11/13 Python
python 调用pyautogui 实时获取鼠标的位置、移动鼠标的方法
2019/08/27 Python
把vgg-face.mat权重迁移到pytorch模型示例
2019/12/27 Python
Python随机数函数代码实例解析
2020/02/09 Python
Python连接HDFS实现文件上传下载及Pandas转换文本文件到CSV操作
2020/06/06 Python
Janie and Jack美国官网:GAP旗下的高档童装品牌
2019/09/09 全球购物
全球最大运动品牌的男装、女装和童装官方库存商:A&A Sports
2021/01/17 全球购物
企业管理培训感言
2014/01/27 职场文书
大学学生会竞选演讲稿
2014/04/25 职场文书
清正廉洁演讲稿
2014/05/22 职场文书
党员四风剖析材料
2014/08/27 职场文书
2014医学院领导干部四风对照检查材料思想汇报
2014/09/16 职场文书
医生个人自我剖析材料
2014/10/08 职场文书
2014年公路养护工作总结
2014/12/04 职场文书
2014年除四害工作总结
2014/12/06 职场文书
2016元旦主持人经典开场白台词
2015/12/03 职场文书
践行三严三实心得体会(2016推荐篇)
2016/01/06 职场文书
Django drf请求模块源码解析
2021/06/08 Python
Golang中channel的原理解读(推荐)
2021/10/16 Golang