用Node.js通过sitemap.xml批量抓取美女图片


Posted in Javascript onMay 28, 2015

之前看了很多个版本,自己也搞一个。

1. 支持指定保存到哪个目录
2. 按文章进行分目录存放
3. 支持设置并行下载上限

下次有空再搞个整站下载的。

package.json

{
 "name": "me2sex-images",
 "version": "0.0.1",
 "description": "Batch download images from http://me2-sex.lofter.com",
 "main": "index.js",
 "author": "Fay",
 "license": "MIT",
 "dependencies": {
  "async": "^0.9.0",
  "cheerio": "^0.18.0",
  "mkdirp": "^0.5.0",
  "request": "^2.51.0",
  "url": "^0.10.2",
  "xml2js": "^0.4.4"
 }
}

index.js

var node = {
  async: require('async'),
  cheerio: require('cheerio'),
  fs: require('fs'),
  mkdirp: require('mkdirp'),
  path: require('path'),
  request: require('request'),
  url: require('url'),
  xml2js: require('xml2js'),
};
 
var Me2SexImages = {
 
  /**
   * 配置选项
   */
  options: {
    // 网站sitemap地址
    sitemap: 'http://sexy.faceks.com/sitemap.xml',
    // 保存到此文件夹
    saveTo: '/Users/Fay/Pictures/me2sex',
    // 图片并行下载上限
    downLimit: 5,
  },
 
  posts: [],
 
  /**
   * 开始下载(程序入口函数)
   */
  start: function() {
    var self = this;
    var async = node.async;
 
    async.waterfall([
      self.wrapTask(self.sitemapXML),
      self.wrapTask(self.sitemapJSON),
      self.wrapTask(self.downAllImages),
    ], function(err, result) {
      if (err) {
        console.log('error: %s', err.message);
      } else {
        console.log('success: 下载成功');
      }
    });
  },
 
  /**
   * 包裹任务,确保原任务的上下文指向某个特定对象
   * @param {Function} task 符合asycs.js调用方式的任务函数
   * @param {Any} context 上下文
   * @param {Array} exArgs 额外的参数
   * @return {Function} 符合asycs.js调用方式的任务函数
   */
  wrapTask: function(task, context, exArgs) {
    var self = this;
    return function() {
      var args = [].slice.call(arguments);
      args = exArgs ? exArgs.concat(args) : args;
      task.apply(context || self, args);
    };
  },
 
  /**
   * 获取站点sitemap.xml
   */
  sitemapXML: function(callback) {
    console.log('开始下载sitemap.xml');
    node.request(this.options.sitemap, function(err, res, body) {
      if (!err) console.log('下载sitemap.xml成功');
      callback(err, body);
    });
  },
 
  /**
   * 将sitemap.xml转成json
   */
  sitemapJSON: function(sitemapXML, callback) {
    var self = this;
    console.log('开始解析sitemap.xml');
    node.xml2js.parseString(sitemapXML, {explicitArray: false}, function(err, json) {
      if (!err) {
        self.posts = json.urlset.url;
        self.posts.shift();
        console.log('解析sitemap.xml成功,共有%d个页面', self.posts.length);
      }
      callback(err, self.posts);
    });
  },
 
 
 
  /**
   * 下载整站图片
   */
  downAllImages: function(callback) {
    var self = this;
    var async = node.async;
    console.log('开始批量下载');
    async.eachSeries(self.posts, self.wrapTask(self.downPostImages), callback);
  },
 
 
  /**
   * 下载单个post的图片
   * @param {Object} post 文章
   */
  downPostImages: function(post, callback) {
    var self = this;
    var async = node.async;
 
    async.waterfall([
      self.wrapTask(self.mkdir, self, [post]),
      self.wrapTask(self.getPost),
      self.wrapTask(self.parsePost),
      self.wrapTask(self.downImages),
    ], callback);
  },
 
  mkdir: function(post, callback) {
    var path = node.path;
    var url = node.url.parse(post.loc);
    post.dir = path.join(this.options.saveTo, path.basename(url.pathname));
 
    console.log('准备创建目录:%s', post.dir);
    if (node.fs.existsSync(post.dir)) {
      callback(null, post);
      console.log('目录:%s 已经存在', post.dir);
      return;
    }
    node.mkdirp(post.dir, function(err) {
      callback(err, post);
      console.log('目录:%s 创建成功', post.dir);
    });
  },
 
  /**
   * 获取post内容
   */
  getPost: function(post, callback) {
    console.log('开始请求页面:%s', post.loc);
    node.request(post.loc, function(err, res, body) {
      if (!err) post.html = body;
      callback(err, post);
      console.log('请求页面成功:%s', post.loc);
    });
  },
 
  /**
   * 解析post,并获取post中的图片列表
   */
  parsePost: function(post, callback) {
    var $ = post.$ = node.cheerio.load(post.html);
    post.images = $('.img')
      .map(function() {return $(this).attr('bigimgsrc');})
      .toArray();
    callback(null, post);
  },
 
  /**
   * 下载post图片列表中的图片
   */
  downImages: function(post, callback) {
    console.log('发现%d张妹子图片,准备开始下载...', post.images.length);
    node.async.eachLimit(
      post.images,
      this.options.downLimit,
      this.wrapTask(this.downImage, this, [post]),
      callback
    );
  },
 
  /**
   * 下载单个图片
   */
  downImage: function(post, imgsrc, callback) {
    var url = node.url.parse(imgsrc);
    var fileName = node.path.basename(url.pathname);
    var toPath = node.path.join(post.dir, fileName);
    console.log('开始下载图片:%s,保存到:%s,文件名:%s', imgsrc, post.dir, fileName);
    node.request(imgsrc)
      .pipe(node.fs.createWriteStream(toPath))
      .on('close', function() {
        console.log('图片下载成功:%s', imgsrc);
        callback();
      })
      .on('error', callback);
  }
};
 
Me2SexImages.start();

以上所述就是本文的全部内容,希望大家能够喜欢。

Javascript 相关文章推荐
jQuery UI 应用不同Theme的办法
Sep 12 Javascript
javascript AOP 实现ajax回调函数使用比较方便
Nov 20 Javascript
ModelDialog JavaScript模态对话框类代码
Apr 17 Javascript
自己动手实现jQuery Callbacks完整功能代码详解
Nov 25 Javascript
JS小游戏之极速快跑源码详解
Sep 25 Javascript
jquery ui bootstrap 实现自定义风格
Nov 14 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【二】
May 10 Javascript
浅谈jquery选择器 :first与:first-child的区别
Nov 20 Javascript
jQuery.parseJSON()函数详解
Feb 28 jQuery
JavaScript的级联函数用法简单示例【链式调用】
Mar 26 Javascript
前端开发之便利店收银系统代码
Dec 27 Javascript
Vue基于iview实现登录密码的显示与隐藏功能
Mar 06 Javascript
javascript转换静态图片,增加粒子动画效果
May 28 #Javascript
jQuery实现限制textarea文本框输入字符数量的方法
May 28 #Javascript
javascript实现行拖动的方法
May 27 #Javascript
JavaScript操作Cookie方法实例分析
May 27 #Javascript
JavaScript通过事件代理高亮显示表格行的方法
May 27 #Javascript
jquery预加载图片的方法
May 27 #Javascript
jQuery仿gmail实现fixed布局的方法
May 27 #Javascript
You might like
php cli 小技巧
2013/06/03 PHP
解决Codeigniter不能上传rar和zip压缩包问题
2014/03/07 PHP
浅谈php优化需要注意的地方
2014/11/27 PHP
php二维码生成以及下载实现
2017/09/28 PHP
QQ登录简单实现代码
2021/03/09 Javascript
JavaScript 学习小结(适合新手参考)
2009/07/30 Javascript
用 Javascript 验证表单(form)中的单选(radio)值
2009/09/08 Javascript
Extjs gridpanel 出现横向滚动条问题的解决方法
2011/07/04 Javascript
JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型
2012/10/11 Javascript
Node.js实现JS文件合并小工具
2016/02/02 Javascript
详解javascript获取url信息的常见方法
2016/12/19 Javascript
JS运动特效之完美运动框架实例分析
2018/01/24 Javascript
JavaScript原型链与继承操作实例总结
2018/08/24 Javascript
vue+vuex+json-seiver实现数据展示+分页功能
2019/04/11 Javascript
JS sort方法基于数组对象属性值排序
2020/07/10 Javascript
微信小程序组件生命周期的踩坑记录
2021/03/03 Javascript
[01:13]这,就是刀塔
2014/07/16 DOTA
python实现socket端口重定向示例
2014/02/10 Python
python中print的不换行即时输出的快速解决方法
2016/07/20 Python
python中enumerate() 与zip()函数的使用比较实例分析
2019/09/03 Python
全网首秀之Pycharm十大实用技巧(推荐)
2020/04/27 Python
Python实现图片指定位置加图片水印(附Pyinstaller打包exe)
2021/03/04 Python
html5的canvas实现3d雪花飘舞效果
2013/12/27 HTML / CSS
英国巧克力贸易公司:Chocolate Trading Company
2017/03/21 全球购物
梅西百货澳大利亚:Macy’s Australia
2017/07/26 全球购物
英国豪华文具和皮具配件经典老品牌:Smythson(斯迈森)
2018/04/19 全球购物
开普敦通行证:Cape Town Pass
2019/07/18 全球购物
职业教育毕业生求职信
2013/11/09 职场文书
心理健康教育心得体会
2013/12/29 职场文书
有兼职工作经历的简历自我评价
2014/03/07 职场文书
运动会400米加油稿(8篇)
2014/09/22 职场文书
个人投资合作协议书
2014/10/12 职场文书
个人股份转让协议书范本
2014/10/26 职场文书
2014年电教工作总结
2014/12/19 职场文书
MySQL 重写查询语句的三种策略
2021/05/10 MySQL
一文了解MYSQL三大范式和表约束
2022/04/03 MySQL