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 相关文章推荐
Extjs中通过Tree加载右侧TabPanel具体实现
May 05 Javascript
使用jQuery异步加载 JavaScript脚本解决方案
Apr 20 Javascript
AngularJS基础学习笔记之表达式
May 10 Javascript
两种JS实现屏蔽鼠标右键的方法
Aug 20 Javascript
jquery ajax局部加载方法详解(实现代码)
May 12 Javascript
使用JSON作为函数的参数的优缺点
Oct 27 Javascript
利用jQuery插件imgAreaSelect实现获得选择域的图像信息
Dec 02 Javascript
JS实现的简单图片切换功能示例【测试可用】
Feb 14 Javascript
vue路由导航守卫和请求拦截以及基于node的token认证的方法
Apr 07 Javascript
vue2路由方式--嵌套路由实现方法分析
Mar 06 Javascript
vue webpack build资源相对路径的问题及解决方法
Jun 04 Javascript
vue实现表格合并功能
Dec 01 Vue.js
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
全国FM电台频率大全 - 28 甘肃省
2020/03/11 无线电
PHP中遍历stdclass object的实现代码
2011/06/09 PHP
php字符串替换函数substr_replace()用法实例
2015/03/17 PHP
php flush无效,IIS7下php实时输出的方法
2016/08/25 PHP
php将print_r处理后的数据还原为原始数组的解决方法
2016/11/02 PHP
php5.3后静态绑定用法详解
2016/11/11 PHP
php使用scandir()函数扫描指定目录下所有文件示例
2019/06/08 PHP
php+redis实现消息队列功能示例
2019/09/19 PHP
jquery select(列表)的操作(取值/赋值)
2009/08/06 Javascript
jQuery的学习步骤
2011/02/23 Javascript
关于window.pageYOffset和document.documentElement.scrollTop
2011/04/05 Javascript
Bootstrap入门书籍之(一)排版
2016/02/17 Javascript
使用JavaScript实现弹出层效果的简单实例
2016/05/31 Javascript
gulp-uglify 与gulp.watch()配合使用时报错(重复压缩问题)
2016/08/24 Javascript
jQuery插件FusionCharts绘制2D双折线图效果示例【附demo源码】
2017/04/14 jQuery
详解ES6之async+await 同步/异步方案
2017/09/19 Javascript
Vue中render方法的使用详解
2018/01/26 Javascript
jQuery实现下拉菜单动态添加数据点击滑出收起其他功能
2018/06/14 jQuery
vue操作dom元素的3种方法示例
2020/09/20 Javascript
pyqt4教程之widget使用示例分享
2014/03/07 Python
Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例
2017/08/07 Python
python读取文本中的坐标方法
2018/10/14 Python
Python操作json的方法实例分析
2018/12/06 Python
Python实现多线程/多进程的TCP服务器
2019/09/03 Python
用python画一只可爱的皮卡丘实例
2019/11/21 Python
公认8个效率最高的爬虫框架
2020/07/28 Python
Python解析微信dat文件的方法
2020/11/30 Python
Html5实现二维码扫描并解析
2016/01/20 HTML / CSS
印度民族服装购物网站:BIBA
2019/08/05 全球购物
维德科技C#面试题笔试题
2015/12/09 面试题
法律专业自我鉴定
2013/10/03 职场文书
医药学专业大学生职业生涯规划书论文
2014/01/21 职场文书
年会主持词结束语
2014/03/27 职场文书
2014年幼儿园班级工作总结
2014/12/17 职场文书
详解Django中 render() 函数的使用方法
2021/04/22 Python
Win11 KB5015814遇安装失败 影响开始菜单性能解决方法
2022/07/15 数码科技