用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的日期联动实现代码
Feb 24 Javascript
javascript截取字符串(通过substring实现并支持中英文混合)
Jun 24 Javascript
实例讲解JS中数组Array的操作方法
May 09 Javascript
使用insertAfter()方法在现有元素后添加一个新元素
May 28 Javascript
JQuery实现展开关闭层的方法
Feb 17 Javascript
jquery插件bxslider用法实例分析
Apr 16 Javascript
JS简单实现动画弹出层效果
May 05 Javascript
在JavaScript中使用NaN值的方法
Jun 05 Javascript
jquery的checkbox,radio,select等方法小结
Aug 30 Javascript
AngularJS实现单独作用域内的数据操作
Sep 05 Javascript
Bootstrap复选框和单选按钮美化插件(推荐)
Nov 23 Javascript
微信小程序获取手机号授权用户登录功能
Nov 09 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源代码
2013/06/26 PHP
PHP图片上传代码
2013/11/04 PHP
php判断正常访问和外部访问的示例
2014/02/10 PHP
PHP的error_reporting错误级别变量对照表
2014/07/08 PHP
php_pdo 预处理语句详解
2016/11/21 PHP
php redis实现文章发布系统(用户投票系统)
2017/03/04 PHP
详解Yii2.0 rules验证规则集合
2017/03/21 PHP
PHP中number_format()函数的用法讲解
2019/04/08 PHP
复制Input内容的js代码_支持所有浏览器,修正了Firefox3.5以上的问题
2010/06/21 Javascript
原生Js实现元素渐隐/渐现(原理为修改元素的css透明度)
2013/06/24 Javascript
JS清空多文本框、文本域示例代码
2014/02/24 Javascript
ExtJS4给Combobox设置列表中的默认值示例
2014/05/02 Javascript
jQuery对val和atrr("value")赋值的区别介绍
2014/09/26 Javascript
Javascript简单实现面向对象编程继承实例代码
2015/11/27 Javascript
jQuery ajax时间差导致的变量赋值问题分析
2016/01/22 Javascript
JS实现的相册图片左右滚动完整实例
2016/11/23 Javascript
jquery表单验证实例仿Toast提示效果
2017/03/03 Javascript
Angular中实现树形结构视图实例代码
2017/05/05 Javascript
Vue+SpringBoot开发V部落博客管理平台
2017/12/27 Javascript
vue使用ajax获取后台数据进行显示的示例
2018/08/09 Javascript
详解ECMAScript2019/ES10新属性
2019/12/06 Javascript
python实现保存网页到本地示例
2014/03/16 Python
Python使用Phantomjs截屏网页的方法
2018/05/17 Python
python 解决动态的定义变量名,并给其赋值的方法(大数据处理)
2018/11/10 Python
python 实现分组求和与分组累加求和代码
2020/05/18 Python
在Ubuntu中安装并配置Pycharm教程的实现方法
2021/01/06 Python
浅析两列自适应布局的3种思路
2016/05/03 HTML / CSS
中专自我鉴定范文
2013/10/16 职场文书
毕业生优秀推荐信
2013/11/26 职场文书
单身联谊活动方案
2014/01/29 职场文书
班主任对学生的评语
2014/04/26 职场文书
信用社主任竞聘演讲稿
2014/05/23 职场文书
委托函范文
2015/01/29 职场文书
小学运动会前导词
2015/07/20 职场文书
解决Navicat for MySQL 连接 MySQL 报2005错误的问题
2021/05/29 MySQL
Java8利用Stream对列表进行去除重复的方法详解
2022/04/14 Java/Android