用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 相关文章推荐
js jquery做的图片连续滚动代码
Jan 06 Javascript
javascript中的继承实例代码
Apr 27 Javascript
javascript如何动态加载表格与动态添加表格行
Nov 27 Javascript
在每个匹配元素的外部插入新元素的方法
Dec 20 Javascript
js控制input输入字符解析
Dec 27 Javascript
JS实现带圆弧背景渐变效果的导航菜单代码
Oct 13 Javascript
jQuery实现鼠标滑过链接控制图片的滑动展开与隐藏效果
Oct 28 Javascript
浅析BootStrap栅格系统
Jun 07 Javascript
Javascript对象字面量的理解
Jun 22 Javascript
vue 开发一个按钮组件的示例代码
Mar 27 Javascript
Vue使用JSEncrypt实现rsa加密及挂载方法
Feb 07 Javascript
vue + node如何通过一个Txt文件批量生成MP3并压缩成Zip
Jun 02 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和ACCESS写聊天室(二)
2006/10/09 PHP
codeigniter教程之上传视频并使用ffmpeg转flv示例
2014/02/13 PHP
php修改上传图片尺寸的方法
2015/04/14 PHP
php通过baihui网API实现读取word文档并展示
2015/06/22 PHP
PHP模糊查询的实现方法(推荐)
2016/09/06 PHP
laravel自定义分页效果
2017/07/23 PHP
使用XHProf查找PHP性能瓶颈的实例
2017/12/13 PHP
CLASS_CONFUSION JS混淆 全源码
2007/12/12 Javascript
Jquery获得控件值的三种方法总结
2014/02/13 Javascript
jquery中表单 多选框的一种巧妙写法
2015/09/06 Javascript
JavaScript表单验证实例之验证表单项是否为空
2016/01/10 Javascript
20分钟成功编写bootstrap响应式页面 就这么简单
2016/05/12 Javascript
JS 通过系统时间限定动态添加 select option的实例代码
2016/06/09 Javascript
JS不完全国际化&本地化手册 之 理论篇
2016/09/27 Javascript
js遍历获取表格内数据的方法(必看)
2017/04/06 Javascript
React-router中结合webpack实现按需加载实例
2017/05/25 Javascript
jQuery实现checkbox即点即改批量删除及中间遇到的坑
2017/11/11 jQuery
在vue-cli项目中使用bootstrap的方法示例
2018/04/21 Javascript
详细分析React 表单与事件
2020/07/08 Javascript
微信小程序完美解决scroll-view高度自适应问题的方法
2020/08/08 Javascript
[41:20]2014 DOTA2华西杯精英邀请赛 5 24 NewBee VS DK
2014/05/26 DOTA
python实现在字符串中查找子字符串的方法
2015/07/11 Python
python 文件操作删除某行的实例
2017/09/04 Python
python logging重复记录日志问题的解决方法
2018/07/12 Python
pandas通过loc生成新的列方法
2018/11/28 Python
对python For 循环的三种遍历方式解析
2019/02/01 Python
python IDLE 背景以及字体大小的修改方法
2019/07/12 Python
详解用Python为直方图绘制拟合曲线的两种方法
2019/08/21 Python
Django admin.py 在修改/添加表单界面显示额外字段的方法
2019/08/22 Python
python GUI库图形界面开发之PyQt5不规则窗口实现与显示GIF动画的详细方法与实例
2020/03/09 Python
python pymysql链接数据库查询结果转为Dataframe实例
2020/06/05 Python
python中温度单位转换的实例方法
2020/12/27 Python
简单而又朴实的个人求职信分享
2013/12/12 职场文书
大学军训感言1000字
2014/02/25 职场文书
2014三年级班主任工作总结
2014/12/05 职场文书
收入证明怎么写
2015/06/12 职场文书