用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 textarea的长度进行验证
May 06 Javascript
js 控制下拉菜单刷新的方法
Mar 03 Javascript
EditPlus注册码生成器(js代码实现)
Mar 25 Javascript
jquery滚动加载数据的方法
Mar 09 Javascript
javascript手风琴下拉菜单实现代码
Nov 12 Javascript
javascript数组去重小结
Mar 07 Javascript
使用AJAX实现Web页面进度条的实例分享
May 06 Javascript
利用node.js本地搭建HTTP服务器
Apr 19 Javascript
Vue实战之vue登录验证的实现代码
Oct 31 Javascript
详解react、redux、react-redux之间的关系
Apr 11 Javascript
JS原型与继承操作示例
May 09 Javascript
vue使用echarts画组织结构图
Feb 06 Vue.js
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相当简单的分页类
2008/10/02 PHP
PHP对象递归引用造成内存泄漏分析
2014/08/28 PHP
PHP实现对png图像进行缩放的方法(支持透明背景)
2015/07/15 PHP
php操作mongodb封装类与用法实例
2018/09/01 PHP
PHP利用pdo_odbc实现连接数据库示例【基于ThinkPHP5.1搭建的项目】
2019/05/13 PHP
学习ExtJS Column布局
2009/10/08 Javascript
chrome下img加载对height()的影响示例探讨
2014/05/26 Javascript
jQuery事件绑定和委托实例
2014/11/25 Javascript
了不起的node.js读书笔记之mongodb数据库交互
2014/12/22 Javascript
JavaScript阻止事件冒泡示例分享
2014/12/28 Javascript
JavaScript基础知识之方法汇总结
2016/01/24 Javascript
jQuery简单实现仿京东分类导航层效果
2016/06/07 Javascript
JavaScript常用正则函数用法示例
2017/01/23 Javascript
JavaScript Drum Kit 指南(纯 JS 模拟敲鼓效果)
2017/07/23 Javascript
Nodejs实现文件上传的示例代码
2017/09/26 NodeJs
vue2.0 实现富文本编辑器功能
2019/05/26 Javascript
微信小程序webview与h5通过postMessage实现实时通讯的实现
2019/08/20 Javascript
vue实现的多页面项目如何优化打包的步骤详解
2020/07/19 Javascript
vue打包通过image-webpack-loader插件对图片压缩优化操作
2020/11/12 Javascript
[46:43]DOTA2上海特级锦标赛D组小组赛#1 EG VS COL第三局
2016/02/28 DOTA
Python中使用SAX解析xml实例
2014/11/21 Python
Python实现删除当前目录下除当前脚本以外的文件和文件夹实例
2015/07/27 Python
django上传图片并生成缩略图方法示例
2017/12/11 Python
Python实现的栈(Stack)
2018/01/26 Python
Python实现模拟浏览器请求及会话保持操作示例
2018/07/30 Python
python 实现将文件或文件夹用相对路径打包为 tar.gz 文件的方法
2019/06/10 Python
python实现的批量分析xml标签中各个类别个数功能示例
2019/12/30 Python
Python-numpy实现灰度图像的分块和合并方式
2020/01/09 Python
利用python实现.dcm格式图像转为.jpg格式
2020/01/13 Python
机械设计职业生涯规划书
2013/12/27 职场文书
培训班开班仪式主持词
2014/03/28 职场文书
生产文员岗位职责
2014/04/05 职场文书
《宋庆龄故居的樟树》教学反思
2014/04/07 职场文书
《彩色世界》教学反思
2014/04/12 职场文书
企业法律事务工作总结
2015/08/11 职场文书
话题作文之呼唤
2019/12/18 职场文书