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 相关文章推荐
“不能执行已释放的Script代码”错误的原因及解决办法
Sep 09 Javascript
JavaScript中两个感叹号的作用说明
Dec 28 Javascript
DOM基础教程之使用DOM控制表格
Jan 20 Javascript
js运动应用实例解析
Dec 28 Javascript
使用JQuery实现智能表单验证功能
Mar 08 Javascript
AJAX实现瀑布流触发分页与分页触发瀑布流的方法
May 23 Javascript
js实现四舍五入完全保留两位小数的方法
Aug 02 Javascript
微信小程序 wx.request(OBJECT)发起请求详解
Oct 13 Javascript
微信小程序日历组件calendar详解及实例
Jun 08 Javascript
父组件中vuex方法更新state子组件不能及时更新并渲染的完美解决方法
Apr 25 Javascript
云服务器部署Node.js项目的方法步骤(小白系列)
Mar 23 Javascript
js实现自动锁屏功能
Jun 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
PHP实现货币换算的方法
2014/11/29 PHP
PHP获取文件相对路径的方法
2015/02/26 PHP
PHP Reflection API详解
2015/05/12 PHP
ThinkPHP3.2.1图片验证码实现方法
2016/08/19 PHP
高性能WEB开发 flush让页面分块,逐步呈现 flush让页面分块,逐步呈现
2010/06/19 Javascript
JQuery浮动DIV提示信息并自动隐藏的代码
2010/08/29 Javascript
使用jQuery插件创建常规模态窗口登陆效果
2013/08/23 Javascript
分享一则JavaScript滚动条插件源码
2015/03/03 Javascript
JS显示表格内指定行html代码的方法
2015/03/31 Javascript
json定义及jquery操作json的方法
2016/09/29 Javascript
js中json处理总结之JSON.parse
2016/10/14 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
2020/04/15 Javascript
js前端对于大量数据的展示方式及处理方法
2020/12/02 Javascript
js+for循环实现字符串自动转义的代码(把后面的字符替换前面的字符)
2020/12/24 Javascript
python将人民币转换大写的脚本代码
2013/02/10 Python
使用Python的Django和layim实现即时通讯的方法
2018/05/25 Python
Python使用Slider组件实现调整曲线参数功能示例
2019/09/06 Python
python实现连连看游戏
2020/02/14 Python
将自己的数据集制作成TFRecord格式教程
2020/02/17 Python
python3.7+selenium模拟淘宝登录功能的实现
2020/05/26 Python
Python实现弹球小游戏
2020/08/01 Python
django中ImageField的使用详解
2020/12/21 Python
非常漂亮的CSS3百叶窗焦点图动画
2016/02/24 HTML / CSS
HTML5中div、article、section的区别及使用介绍
2013/08/14 HTML / CSS
台湾生鲜宅配:大口市集
2017/10/14 全球购物
澳大利亚礼品篮网站:Macarthur Baskets
2019/10/14 全球购物
阿里巴巴美国:Alibaba美国
2019/11/24 全球购物
复核员上岗演讲稿
2014/01/05 职场文书
会计专业大学生求职信范文
2014/01/28 职场文书
GMP办公室主任岗位职责
2014/03/14 职场文书
大学生通用个人自我评价
2014/04/27 职场文书
怎样写辞职信
2015/02/27 职场文书
供应商食品安全承诺书
2015/04/29 职场文书
2016国培学习心得体会
2016/01/08 职场文书
Python Socket编程详解
2021/04/25 Python
MySQL数据库实验实现简单数据库应用系统设计
2022/06/21 MySQL