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 相关文章推荐
JS图片根据鼠标滚动延时加载的实例代码
Jul 13 Javascript
jquery ajax方式直接提交整个表单核心代码
Aug 15 Javascript
用jQuery实现的智能隐藏、滑动效果的返回顶部代码
Mar 18 Javascript
jQuery设置和获取HTML、文本和值示例
Jul 08 Javascript
超炫的jquery仿flash导航栏特效
Nov 11 Javascript
json实现添加、遍历与删除属性的方法
Jun 17 Javascript
Highcharts入门之基本属性
Aug 02 Javascript
js字符串引用的两种方式(必看)
Sep 18 Javascript
React Native如何消除启动时白屏的方法
Aug 08 Javascript
微信小程序promsie.all和promise顺序执行
Oct 27 Javascript
jQuery 添加元素和删除元素的方法
Jul 15 jQuery
解决vue打包 npm run build-test突然不动了的问题
Nov 13 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
使用 php4 加速 web 传输
2006/10/09 PHP
ueditor 1.2.6 使用方法说明
2013/07/24 PHP
PHP中读取文件的8种方法和代码实例
2014/08/05 PHP
详解PHP序列化反序列化的方法
2015/10/27 PHP
js 替换
2008/02/19 Javascript
响应鼠标变换表格背景或者颜色的代码
2009/03/30 Javascript
文本框的字数限制功能jquery插件
2009/11/24 Javascript
jquery时间下拉框小例子
2013/04/15 Javascript
js获得地址栏?问号后参数的方法
2013/08/08 Javascript
JSuggest自动匹配下拉框使用方法(示例代码)
2013/12/27 Javascript
IE下通过a实现location.href 获取referer的值
2014/09/04 Javascript
javascript结合Canvas 实现简易的圆形时钟
2015/03/11 Javascript
JS+CSS实现仿msn风格选项卡效果代码
2015/10/22 Javascript
基于jQuery实现自动轮播旋转木马特效
2015/11/02 Javascript
SVG描边动画
2017/02/23 Javascript
jQuery插件HighCharts实现的2D回归直线散点效果示例【附demo源码下载】
2017/03/09 Javascript
jquery ui sortable拖拽后保存位置
2017/04/27 jQuery
bootstrap multiselect 多选功能实现方法
2017/06/05 Javascript
浅析 NodeJs 的几种文件路径
2017/06/07 NodeJs
JS倒计时两种实现方式代码实例
2020/07/27 Javascript
[03:14]DOTA2斧王 英雄基础教程
2013/11/26 DOTA
python 使用re.search()筛选后 选取部分结果的方法
2018/11/28 Python
解决Python运行文件出现out of memory框的问题
2018/12/03 Python
python生成每日报表数据(Excel)并邮件发送的实例
2019/02/03 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
centos+nginx+uwsgi+Django实现IP+port访问服务器
2019/11/15 Python
解决python replace函数替换无效问题
2020/01/18 Python
美国折扣地毯销售网站:Rugs.com
2020/03/27 全球购物
应届生会计电算化求职信
2013/10/03 职场文书
入团者的自我评价分享
2013/12/02 职场文书
2014自荐信的写作技巧
2014/01/28 职场文书
公务员更新知识培训实施方案
2014/03/31 职场文书
干部对照检查材料范文
2014/08/26 职场文书
Mongo服务重启异常问题的处理方法
2021/07/01 MongoDB
HTML5 新增内容和 API详解
2021/11/17 HTML / CSS