用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 相关文章推荐
javascript attachEvent和addEventListener使用方法
Mar 19 Javascript
HTML中的setCapture和releaseCapture使用介绍
Mar 21 Javascript
javascript之典型高阶函数应用介绍
Jan 10 Javascript
javascript中对Attr(dom中属性)的操作示例讲解
Dec 02 Javascript
FF IE浏览器修改标签透明度的方法
Jan 27 Javascript
IE中鼠标经过option触发mouseout的解决方法
Jan 29 Javascript
JavaScript设置、获取、清除单值和多值cookie的方法
Nov 17 Javascript
jQuery简单倒计时效果完整示例
Sep 20 Javascript
十大热门的JavaScript框架和库
Mar 21 Javascript
jQuery常见的遍历DOM操作详解
Sep 05 jQuery
开源一个微信小程序仪表盘组件过程解析
Jul 30 Javascript
layui数据表格重载实现往后台传参
Nov 15 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中使用$_REQUEST需要注意的一个问题
2013/05/02 PHP
PHP面向对象精要总结
2014/11/07 PHP
PHP模板引擎Smarty内建函数详解
2016/04/11 PHP
PHP中类的自动加载的方法
2017/03/17 PHP
javascript下数值型比较难点说明
2010/06/07 Javascript
Jquery实现弹出层分享微博插件具备动画效果
2013/04/03 Javascript
基于jQuery实现图片的前进与后退功能
2013/04/24 Javascript
jQuery获取CSS样式中的颜色值的问题,不同浏览器格式不同的解决办法
2013/05/13 Javascript
js小数运算出现多位小数如何解决
2015/10/08 Javascript
js停止冒泡和阻止浏览器默认行为的简单方法
2016/05/15 Javascript
JavaScript地理位置信息API
2016/06/11 Javascript
简单理解vue中track-by属性
2016/10/26 Javascript
js实现文本上下来回滚动
2017/02/03 Javascript
从零学习node.js之文件操作(三)
2017/02/21 Javascript
vue实现微信分享链接添加动态参数的方法
2019/04/29 Javascript
layui表格数据重载
2019/07/27 Javascript
layui table去掉右侧滑动条的实现方法
2019/09/05 Javascript
vue 遮罩层阻止默认滚动事件操作
2020/07/28 Javascript
[51:00]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
Python Web服务器Tornado使用小结
2014/05/06 Python
python两种遍历字典(dict)的方法比较
2014/05/29 Python
Python3中urlencode和urldecode的用法详解
2019/07/23 Python
浅析pip安装第三方库及pycharm中导入第三方库的问题
2020/03/10 Python
Pycharm自动添加文件头注释和函数注释参数的方法
2020/10/23 Python
详解Python中openpyxl模块基本用法
2021/02/23 Python
英国排名第一的餐具品牌:Denby Pottery
2019/11/01 全球购物
俄罗斯最大的灯具网站:Fandeco
2020/03/14 全球购物
MediaMarkt比利时:欧洲最大电器连锁店
2020/12/21 全球购物
英国领先的独立酒精饮料零售商:DrinkSupermarket
2021/01/13 全球购物
小学生春游活动方案
2014/08/20 职场文书
工商局局长个人对照检查材料思想汇报
2014/09/23 职场文书
个人租房协议书
2014/11/28 职场文书
倡议书范文大全
2015/04/28 职场文书
手术室消毒隔离制度
2015/08/05 职场文书
导游词之黄果树瀑布
2019/09/20 职场文书
redis使用不当导致应用卡死bug的过程解析
2021/07/01 Redis