用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 相关文章推荐
Dom加载让图片加载完再执行的脚本代码
May 15 Javascript
Javascript 中的 call 和 apply使用介绍
Feb 22 Javascript
jquery实现鼠标点击后展开列表内容的导航栏效果
Sep 14 Javascript
理解Angular数据双向绑定
Jan 10 Javascript
又一枚精彩的弹幕效果jQuery实现
Jul 25 Javascript
基于pako.js实现gzip的压缩和解压功能示例
Jun 13 Javascript
JS基于贪心算法解决背包问题示例
Nov 27 Javascript
jquery动态添加以及遍历option并获取特定样式名称的option方法
Jan 29 jQuery
vue 组件使用中的一些细节点
Apr 25 Javascript
Vue使用高德地图搭建实时公交应用功能(地图 + 附近站点+线路详情 + 输入提示+换乘详情)
May 16 Javascript
深入了解javascript 数组的sort方法
Jun 01 Javascript
js实现无缝滚动双图切换效果
Jul 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输出xml属性的方法
2015/03/19 PHP
Joomla简单判断用户是否登录的方法
2016/05/04 PHP
php封装的page分页类完整实例
2016/10/18 PHP
搜索附近的人PHP实现代码
2018/02/11 PHP
thinkPHP5框架闭包函数与子查询传参用法示例
2018/08/02 PHP
Javascript基础教程之数据类型 (字符串 String)
2015/01/18 Javascript
javascript实现ecshop搜索框键盘上下键切换控制
2015/03/18 Javascript
JavaScript实现将UPC转换成ISBN的方法
2015/05/26 Javascript
浅谈javascript中基本包装类型
2015/06/03 Javascript
Javascript闭包实例详解
2015/11/29 Javascript
JavaScript中字符串与Unicode编码互相转换的实现方法
2015/12/18 Javascript
JS获取屏幕高度的简单实现代码
2016/05/24 Javascript
js操作数据库实现注册和登陆的简单实例
2016/05/26 Javascript
深入浅析JavaScript的API设计原则
2016/06/14 Javascript
妙用Bootstrap的 popover插件实现校验表单提示功能
2016/08/29 Javascript
微信小程序 绘图之饼图实现
2016/10/24 Javascript
微信小程序 网络API 上传、下载详解
2016/11/09 Javascript
基于javascript实现的快速排序
2016/12/02 Javascript
详解Vue.js 2.0 如何使用axios
2017/04/21 Javascript
vue-rx的初步使用教程
2018/09/21 Javascript
详解小程序循环require之坑
2019/03/08 Javascript
Nodejs实现WebSocket代码实例
2020/05/19 NodeJs
在vue中实现禁止屏幕滚动,禁止屏幕滑动
2020/07/22 Javascript
详解Vue的mixin策略
2020/11/19 Vue.js
python 用for循环实现1~n求和的实例
2019/02/01 Python
python 在threading中如何处理主进程和子线程的关系
2020/04/25 Python
Python如何将函数值赋给变量
2020/04/28 Python
python高级特性简介
2020/08/13 Python
娇韵诗加拿大官网:Clarins加拿大
2017/11/20 全球购物
美国在线购物频道:Shop LC
2019/04/21 全球购物
linux面试题参考答案(5)
2016/11/05 面试题
2015年幼师工作总结
2015/04/28 职场文书
活动新闻稿范文
2015/07/17 职场文书
宝宝满月宴答谢词
2015/09/30 职场文书
Python包管理工具pip的15 个使用小技巧
2021/05/17 Python
ipad隐藏软件app图标方法
2022/04/19 数码科技