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 null和undefined区别分析
Oct 14 Javascript
jQuery数据显示插件整合实现代码
Oct 24 Javascript
js中的屏蔽的使用示例
Jul 30 Javascript
JS实现带关闭功能的阿里妈妈网站顶部滑出banner工具条代码
Sep 17 Javascript
Form表单按回车自动提交表单的实现方法
Nov 18 Javascript
微信小程序 动画的简单实例
Oct 12 Javascript
js尾调用优化的实现
May 23 Javascript
在Node.js中将SVG图像转换为PNG,JPEG,TIFF,WEBP和HEIF格式的方法
Aug 22 Javascript
小程序中this.setData的使用和注意事项
Aug 28 Javascript
微信小程序实现树莓派(raspberry pi)小车控制
Feb 12 Javascript
JavaScript常用工具函数大全
May 06 Javascript
Vue和React有哪些区别
Sep 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
用PHPdig打造属于你自己的Google[图文教程]
2007/02/14 PHP
PHP读取目录下所有文件的代码
2008/01/07 PHP
php中常用字符串处理代码片段整理
2011/11/07 PHP
Laravel5.5 数据库迁移:创建表与修改表示例
2019/10/23 PHP
json跟xml的对比分析
2008/06/10 Javascript
JavaScript 异常处理 详解
2015/02/06 Javascript
Bootstrap每天必学之级联下拉菜单
2016/03/27 Javascript
JS获取屏幕高度的简单实现代码
2016/05/24 Javascript
微信JS接口大全
2016/08/25 Javascript
jquery中$.fn和图片滚动效果实现的必备知识总结
2017/04/21 jQuery
12条写出高质量JS代码的方法
2018/01/07 Javascript
JavaScript实现的贝塞尔曲线算法简单示例
2018/01/30 Javascript
vue路由拦截及页面跳转的设置方法
2018/05/24 Javascript
JS实现的汉字与Unicode码相互转化功能分析
2018/05/25 Javascript
vue-router+nginx 非根路径配置方法
2018/06/30 Javascript
JavaScript中.min.js和.js文件的区别讲解
2019/02/13 Javascript
基于vue-cli搭建多模块且各模块独立打包的项目
2019/06/12 Javascript
jQuery实现判断滚动条滚动到document底部的方法分析
2019/08/27 jQuery
layui实现鼠标移动到单元格上显示数据的方法
2019/09/11 Javascript
使用layui实现树形结构的方法
2019/09/20 Javascript
JS中的变量作用域(console版)
2020/07/18 Javascript
浅谈python新手中常见的疑惑及解答
2016/06/14 Python
Python入门之后再看点什么好?
2018/03/05 Python
对pycharm 修改程序运行所需内存详解
2018/12/03 Python
Python数据可视化库seaborn的使用总结
2019/01/15 Python
Python实现定时自动关闭的tkinter窗口方法
2019/02/16 Python
python实现while循环打印星星的四种形状
2019/11/23 Python
CSS3 background-image颜色渐变的实现代码
2018/09/13 HTML / CSS
甲方资料员岗位职责
2013/12/13 职场文书
农贸市场管理制度
2014/01/31 职场文书
干部下基层实施方案
2014/03/14 职场文书
个人四风问题原因分析及整改措施
2014/09/28 职场文书
2016年“5.12”护士节致辞
2015/07/31 职场文书
大学生十八大感想
2015/08/11 职场文书
公务员的复习计划书,请收下!
2019/07/15 职场文书
JavaScript严格模式不支持八进制的问题讲解
2021/11/07 Javascript