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 13 Javascript
jQuery 联动日历实现代码
May 31 Javascript
JQuery下拉框应用示例介绍
Apr 23 Javascript
Javascript中的call()方法介绍
Mar 15 Javascript
JS实现带缓冲效果打开、关闭、移动一个层的方法
May 09 Javascript
js+html5实现canvas绘制镂空字体文本的方法
Jun 05 Javascript
SpringMVC简单整合Angular2的示例
Jul 31 Javascript
使用JavaScript进行表单校验功能
Aug 01 Javascript
基于js的变量提升和函数提升(详解)
Sep 17 Javascript
layui.js实现的表单验证功能示例
Nov 15 Javascript
jQuery实现checkbox的简单操作
Nov 18 jQuery
微信小程序批量监听输入框对按钮样式进行控制的实现代码
Oct 12 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
基于empty函数的判断详解
2013/06/17 PHP
thinkphp如何获取客户端IP
2015/11/03 PHP
利用PHP访问带有密码的Redis方法示例
2017/02/09 PHP
PHP-FPM 设置多pool及配置文件重写操作示例
2019/10/02 PHP
php设计模式之享元模式分析【星际争霸游戏案例】
2020/03/23 PHP
EASYUI TREEGRID异步加载数据实现方法
2012/08/22 Javascript
jQuery+pjax简单示例汇总
2017/04/21 jQuery
angular实现spa单页面应用实例
2017/07/10 Javascript
jQuery实现table中两列CheckBox只能选中一个的示例
2017/09/22 jQuery
vuex与组件联合使用的方法
2018/05/10 Javascript
vue-cli项目中使用echarts图表实例
2018/10/22 Javascript
详解Vue2.0组件的继承与扩展
2018/11/23 Javascript
微信小程序如何再次获取用户授权的方法
2019/05/10 Javascript
p5.js绘制创意自画像
2019/11/04 Javascript
vue实现动态表格提交参数动态生成控件的操作
2020/11/09 Javascript
[47:43]完美世界DOTA2联赛PWL S3 Magama vs GXR 第二场 12.19
2020/12/24 DOTA
对pandas中apply函数的用法详解
2018/04/10 Python
Python 通配符删除文件的实例
2018/04/24 Python
Django如何防止定时任务并发浅析
2019/05/14 Python
对Python强大的可变参数传递机制详解
2019/06/13 Python
Python实现获取系统临时目录及临时文件的方法示例
2019/06/26 Python
python变量的存储原理详解
2019/07/10 Python
Python整数与Numpy数据溢出问题解决
2019/09/11 Python
windows下python 3.9 Numpy scipy和matlabplot的安装教程详解
2020/11/28 Python
细说CSS3中的选择符
2008/10/17 HTML / CSS
CSS3利用text-shadow属性实现多种效果的文字样式展现方法
2016/08/25 HTML / CSS
MATCHESFASHION澳大利亚/亚太地区:英国时尚奢侈品电商
2020/01/14 全球购物
JAVA中运算符的分类及举例
2015/09/12 面试题
管理专员自荐信
2014/01/26 职场文书
四年大学自我鉴定
2014/02/17 职场文书
房地产广告策划方案
2014/05/15 职场文书
国际贸易专业自荐信
2014/06/10 职场文书
国防教育标语
2014/10/08 职场文书
房屋买卖委托书格式范本格式
2014/10/13 职场文书
就业意向协议书
2015/01/29 职场文书
Tomcat项目启动失败的原因和解决办法
2022/04/20 Servers