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 相关文章推荐
用js读、写、删除Cookie代码续篇
Dec 03 Javascript
jQuery实现的经典滑动门效果
Sep 22 Javascript
node.js使用cluster实现多进程
Mar 17 Javascript
jQuery Ajax File Upload实例源码
Dec 12 Javascript
基于Javascript倒计时效果
Dec 22 Javascript
整理关于Bootstrap警示框的慕课笔记
Mar 29 Javascript
vue双花括号的使用方法 附练习题
Nov 07 Javascript
原生js实现获取form表单数据代码实例
Mar 27 Javascript
浏览器事件循环与vue nextTicket的实现
Apr 16 Javascript
jquery 验证用户名是否重复代码实例
May 14 jQuery
Vue中使用matomo进行访问流量统计的实现
Nov 05 Javascript
javascript实现点击按钮切换轮播图功能
Sep 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
php相当简单的分页类
2008/10/02 PHP
php安全开发 添加随机字符串验证,防止伪造跨站请求
2013/02/14 PHP
php获取当前月与上个月月初及月末时间戳的方法
2016/12/05 PHP
php文件包含目录配置open_basedir的使用与性能详解
2017/04/03 PHP
Swoole4.4协程抢占式调度器详解
2019/05/23 PHP
获取网站跟路径的javascript代码(站点及虚拟目录)
2009/10/20 Javascript
基于jQuery的js分页代码
2010/06/10 Javascript
jQuery.extend 函数的详细用法
2012/06/27 Javascript
P3P Header解决Cookie跨域的问题
2013/03/12 Javascript
鼠标滚轮控制网页横向移动实现思路
2013/03/22 Javascript
详解js中构造流程图的核心技术JsPlumb(2)
2015/12/08 Javascript
Jquery和BigFileUpload实现大文件上传及进度条显示
2016/06/27 Javascript
bootstrap suggest下拉框使用详解
2017/04/10 Javascript
jQuery实现html table行Tr的复制、删除、计算功能
2017/07/10 jQuery
实例详解JavaScript中setTimeout函数的执行顺序
2017/07/12 Javascript
使用重写url机制实现验证码换一张功能
2017/08/01 Javascript
angular第三方包开发整理(小结)
2018/04/19 Javascript
微信小程序使用form表单获取输入框数据的实例代码
2018/05/17 Javascript
教你完全理解ReentrantLock重入锁
2019/06/03 Javascript
JavaScript cookie原理及使用实例
2020/05/08 Javascript
[01:46]2020完美世界全国高校联赛秋季赛报名开启
2020/10/15 DOTA
django 自定义用户user模型的三种方法
2014/11/18 Python
使用Pyrex来扩展和加速Python程序的教程
2015/04/13 Python
Python fileinput模块使用实例
2015/05/28 Python
window下eclipse安装python插件教程
2017/04/24 Python
python 成功引入包但无法正常调用的解决
2020/03/09 Python
keras 多gpu并行运行案例
2020/06/10 Python
Python 如何测试文件是否存在
2020/07/31 Python
python 实现一个图形界面的汇率计算器
2020/11/09 Python
浅析Python打包时包含静态文件处理方法
2021/01/15 Python
python线程优先级队列知识点总结
2021/02/28 Python
审计工作个人的自我评价
2013/12/25 职场文书
结婚典礼证婚词
2014/01/11 职场文书
《口技》教学反思
2014/02/21 职场文书
解除劳动合同协议书范本
2014/04/14 职场文书
电影圆明园观后感
2015/06/03 职场文书