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图片轮播的具体实现
Sep 11 Javascript
Extjs中RowExpander控件的默认展开问题示例探讨
Jan 24 Javascript
使用jQuery仿苹果官网焦点图特效
Dec 23 Javascript
javascript 中的事件委托详解
Oct 25 Javascript
jQuery中DOM节点删除之empty与remove
Jan 20 Javascript
Vue2.0 vue-source jsonp 跨域请求
Aug 04 Javascript
通过fastclick源码分析彻底解决tap“点透”
Dec 24 Javascript
详解create-react-app 自定义 eslint 配置
Jun 07 Javascript
vue实现动态添加数据滚动条自动滚动到底部的示例代码
Jul 06 Javascript
基于vue实现web端超大数据量表格的卡顿解决
Apr 02 Javascript
vue-router为激活的路由设置样式操作
Jul 18 Javascript
在vue中封装的弹窗组件使用队列模式实现方法
Jul 23 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
dedecms采集中可以过滤多行代码的正则表达式
2007/03/17 PHP
php 多线程上下文中安全写文件实现代码
2009/12/28 PHP
php生成随机密码的几种方法
2011/01/17 PHP
ThinkPHP的截取字符串函数无法显示省略号的解决方法
2014/06/25 PHP
解读PHP的Yii框架中请求与响应的处理流程
2016/03/17 PHP
PHP实现的随机IP函数【国内IP段】
2016/07/20 PHP
php 一维数组的循环遍历实现代码
2017/04/10 PHP
php基于数组函数实现关联表的编辑操作示例
2017/07/04 PHP
PHP字符串与数组处理函数用法小结
2020/01/07 PHP
js随机颜色代码的多种实现方式
2013/04/23 Javascript
javascript删除数组元素并且数组长度减小的简单实例
2014/02/14 Javascript
JS实现OCX控件的事件响应示例
2014/09/17 Javascript
jQuery使用empty()方法删除元素及其所有子元素的方法
2015/03/26 Javascript
给before和after伪元素设置js效果的方法
2015/12/04 Javascript
d3.js入门教程之数据绑定详解
2017/04/28 Javascript
图片懒加载imgLazyLoading.js使用详解
2020/09/15 Javascript
解决webpack无法通过IP地址访问localhost的问题
2018/02/22 Javascript
使用 Jest 和 Supertest 进行接口端点测试实例详解
2020/04/25 Javascript
vue+element_ui上传文件,并传递额外参数操作
2020/12/05 Vue.js
[01:00:35]2018DOTA2亚洲邀请赛3月30日B组 EffcetVSMineski
2018/03/31 DOTA
Windows下使Python2.x版本的解释器与3.x共存的方法
2015/10/25 Python
Python实现随机选择元素功能
2017/09/14 Python
python opencv设置摄像头分辨率以及各个参数的方法
2018/04/02 Python
使用selenium和pyquery爬取京东商品列表过程解析
2019/08/15 Python
python数据类型强制转换实例详解
2020/06/22 Python
Python爬虫定时计划任务的几种常见方法(推荐)
2021/01/15 Python
html5新特性与用法大全
2018/09/13 HTML / CSS
使用html5实现表格实现标题合并的实例代码
2019/05/13 HTML / CSS
加拿大国民体育购物网站:National Sports
2018/11/04 全球购物
校园活动策划书范文
2014/01/10 职场文书
英语生日邀请函
2014/01/23 职场文书
高中毕业自我评价
2014/02/08 职场文书
励志演讲稿3分钟
2014/08/21 职场文书
感恩主题班会教案
2015/08/12 职场文书
2016年“我们的节日·清明节”活动总结
2016/04/01 职场文书
nginx location 带斜杠【 / 】与不带的区别
2022/04/13 Servers