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 相关文章推荐
jQuery UI Dialog 创建友好的弹出对话框实现代码
Apr 12 Javascript
小米公司JavaScript面试题
Dec 29 Javascript
浅析四种常见的Javascript声明循环变量的书写方式
Oct 14 Javascript
javascript实现dom元素可拖动
Mar 21 Javascript
Bootstrap编写一个兼容主流浏览器的受众巨幕式风格页面
Jul 01 Javascript
JS实现滑动门效果的方法详解
Dec 19 Javascript
Angular2生命周期钩子函数的详细介绍
Jul 10 Javascript
浅谈Angular路由守卫
Aug 26 Javascript
在node中使用jwt签发与验证token的方法
Apr 03 Javascript
vue router总结 $router和$route及router与 router与route区别
Jul 05 Javascript
vue 解决computed修改data数据的问题
Nov 06 Javascript
JavaScript事件循环及宏任务微任务原理解析
Sep 02 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
php5 mysql分页实例代码
2008/04/10 PHP
mysql 性能的检查和优化方法
2009/06/21 PHP
php树型类实例
2014/12/05 PHP
twig模板常用语句实例小结
2016/02/04 PHP
PHP 匿名函数与注意事项详细介绍
2016/11/26 PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
2020/05/02 PHP
一个小型js框架myJSFrame附API使用帮助
2008/06/28 Javascript
执行iframe中的javascript方法
2008/10/07 Javascript
JQuery 常用方法基础教程
2009/02/06 Javascript
网页编辑器ckeditor和ckfinder配置步骤分享
2012/05/24 Javascript
jquery、js调用iframe父窗口与子窗口元素的方法整理
2014/07/31 Javascript
js的touch事件的实际引用
2014/10/13 Javascript
JavaScript实现文字与图片拖拽效果的方法
2015/02/16 Javascript
JQuery操作元素的css样式
2015/03/09 Javascript
javascript产生随机数方法汇总
2016/01/25 Javascript
jQuery代码实现图片墙自动+手动淡入淡出切换效果
2016/05/09 Javascript
jQuery为某个div加入行样式
2017/06/09 jQuery
详解React中setState回调函数
2018/06/14 Javascript
js打开word文档预览操作示例【不是下载】
2019/05/23 Javascript
手把手教您实现react异步加载高阶组件
2020/04/07 Javascript
python实现颜色rgb和hex相互转换的函数
2015/03/19 Python
spyder常用快捷键(分享)
2017/07/19 Python
django 2.0更新的10条注意事项总结
2018/01/05 Python
python用列表生成式写嵌套循环的方法
2018/11/08 Python
对Python3中列表乘以某一个数的示例详解
2019/07/20 Python
python调用函数、类和文件操作简单实例总结
2019/11/29 Python
Python使用ElementTree美化XML格式的操作
2020/03/06 Python
详解python模块pychartdir安装及导入问题
2020/10/22 Python
用HTML5制作一个简单的弹力球游戏
2015/05/12 HTML / CSS
计算机专业个人求职自荐信
2013/09/21 职场文书
大学专科生推荐信范文
2013/11/23 职场文书
文化与传播毕业生求职信
2014/03/09 职场文书
超市开店计划书
2014/04/26 职场文书
2014年学校党建工作总结
2014/11/11 职场文书
PyQt5爬取12306车票信息程序的实现
2021/05/14 Python
MySQL数据库实验之 触发器和存储过程
2022/06/21 MySQL