如何用Nightmare去爬取一些图片

1、前言

这是博主第一次发博客,博主也是一个刚毕业的小菜鸡,希望大家在看博客的时候,能够多多包涵。
如果遇到一些语法错误,或者某些不合理的地方也希望大家多多留言指点。我都会一一回复的。

2、涉及的工具和依赖

①、Nightmare

基于electron的自动化测试套件,嘿嘿,既然是自动化测试套件,那说明就可以来做爬虫,有搞头

安装方式:

npm install --save nightmare ;
Ps:如果安装失败了或者太慢了,换个淘宝的代理就好啦(一般都是electron的问题)。

②、vo.js

最小的用于进行控制异步操作的工具库(因为该工具库知名度不高,我不知道描述是否正确,如有不对,请大佬指正)

安装方式:

npm install --save vo ;

3、正文

废话少说,show you the code

①、初始化核心

var Nightmare = require("nightmare");   //核心库
const fs = require("fs");              //node.js读写文件
const vo = require("vo");              //控制异步

/**
 * 初始化nightmare
 */
const nightmare = new Nightmare({
  openDevTools: {
    mode: "detach",        
  },
  show: true,                 //调试环境下可以改为false
  executionTimeout: 8640000,    //为了防止evalute()因为超时报错,所以把时间调到最大
});

②、工具函数

  /**
   * 生成文件名的函数
   */
  function uuid() {
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4";
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); 
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
  }

  /**
   * 下载图片的函数 
   */
   function downloadImage(src) {
    /**
     * 因为图片是懒加载的,进入可视窗口后,js给图片src赋予的是base64格式,所以直接保存base64就可以了
     * Ps:如果是网络地址,需要用到https或者request去下载
     */
    let Exp = /^data:image\/\w+;base64,/;
    let base64Data = src.replace(/^data:image\/\w+;base64,/, "");
    let dataBuffer = new Buffer.from(base64Data, "base64");
    // console.log(dataBuffer)
    fs.writeFile(`./img/${uuid()}.png`, dataBuffer, (err) => {
      if (err) {
        console.log("保存失败");
      }
      console.log("保存成功1");
    });
  }

③、主体函数

const run = async () => {
  /**
   * 获取想要爬取的图片列表页
   */
  let href = await nightmare
    //打开指定的网址(河蟹一下网址,怕被封)
    .goto("https://xxxx/index/home.html")
    .evaluate(() => {
      //等待页面加载完成后,在页面里寻找对应需要跳转的页面的地址,并返回该地址
      let a = document.querySelector("a[href='/tupian/list-xxxx.html']");
      return a.href;
    })
    //nightmare所有在evaluate中return的变量都要在then里面接受
    .then((href) => {
      return href;
    });

  /**
   * 获取列表页中所有图集的地址(嘿嘿嘿)
   */
  let urls = await nightmare
    .goto(href)
    // 等待列表页加载完成后
    .evaluate(async () => {
      let body = document.body;
      const getUrls = function () {
        return new Promise((resolve) => {
          // 由于图集地址是懒加载的,所以我们要用定时器来模拟浏览器的滚动事件进行滚动
          let timer = setInterval(() => {
            // 这个600是根据网页的不同而不同的喔
            if (body.scrollTop < body.scrollHeight - 600) {
              body.scrollTop += 200;
              console.log(body.scrollTop, body.scrollHeight);
            } else {
              // 等到浏览器滚动到底部后,收集所有图集的跳转地址
              console.log("done");
              clearInterval(timer);
              let urls = [];
              let nodes = document.querySelectorAll(".tupian-pic");
              console.log(nodes);
              nodes.forEach((item) => {
                urls.push(item.href);
              });
              resolve(urls);
            }
          }, 1000);
        });
      };
      // 将收集到地址返回
      // Ps:其实这里的所有async/await都可以用vo做控制,只是我做到最后才引入的vo
      //所以就导致了代码有些不伦不类
      let urls = await getUrls();
      return urls;
    })
    .then((urls) => {
      return urls;
    });
  console.log(urls);

  /**
   * 
   * 根据返回的图集地址,利用nightmare打开每一个图集,
   * 并收集他们里面的图片
   */
  const getImgSrc = function* (urls) {
    for (let i = 0; i < urls.length; i++) {
      const element = urls[i];
      // 这里的yield 就是vo用来控制异步的标识(如有不对,请指正)
      let imgsrc = yield nightmare
        .goto(element)
        .evaluate(async () => {
          let body = document.body;
          const getUrls = function () {
            // 同上,因为图片区域也是懒加载的,所以我们也要模拟浏览器的操作进行滚动
            return new Promise((resolve) => {
              let timer = setInterval(() => {
                if (body.scrollTop < body.scrollHeight - 600) {
                  body.scrollTop += 200;
                  console.log(body.scrollTop, body.scrollHeight);
                } else {
                  console.log("done");
                  clearInterval(timer);
                  let urls = [];
                  let nodes = document.querySelectorAll(".videopic");
                  console.log(nodes);
                  nodes.forEach((item) => {
                    urls.push(item.src);
                  });
                  resolve(urls);
                }
              }, 1000);
            });
          };
          let urls = await getUrls();
          console.log(urls, "urls");
          return urls;
        })
        .then((urls) => {
          return urls;
        });
      //获取到图片地址后,利用下载函数进行下载 
      imgsrc.forEach((img) => {
        downloadImage(img);
      });
    }
    return;
  };

  // 运行vo
  vo(getImgSrc(urls))((err) => {});

  /**
   * 生成文件名的函数
   */
  function uuid() {
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
  }

  /**
   * 下载图片的函数
   */
  function downloadImage(src) {
    /**
     * 因为图片是懒加载出来,给图片src赋予的是base64格式,所以直接保存base64了
     * Ps:如果是网络地址,需要用到https或者request去下载
     */
    let Exp = /^data:image\/\w+;base64,/;
    let base64Data = src.replace(/^data:image\/\w+;base64,/, "");
    let dataBuffer = new Buffer.from(base64Data, "base64");
    // console.log(dataBuffer)
    fs.writeFile(`./img/${uuid()}.png`, dataBuffer, (err) => {
      if (err) {
        console.log("保存失败");
      }
      console.log("保存成功1");
    });
  }
};
run()

4、成果

就不放具体的图片了,给大家伙看看缩略图吧
成果图片

5、后记

爬取羞羞的图片的基本功能已经完成了。但出于演示,我这里只爬取了第一页图集里面的所有图。
如果想爬取多页的话,只需要用写一个循环去拼接第一步获取的地址
example:
第二页的地址: /tupian/list-xxxx-2.html
Ps:当然可能会有不同的拼接方式,这里只是一个思路
拿到地址后,再用vo做一层封装即可。如果不会的话,可以留言

还有就是某些网页可能会有一些额外的跳转,比如在点击了几个图集之后,就重定向到其他地方,比如广告页等等。
这个时候,你只需要在第二步,即打开每个图集后的evaluate方法体里,加一个js的正则去判断是否是你要爬取的页面,
如果不是则重新跳转到你要爬取的页面即可

6、预告

既然羞羞的图片爬完了,那下一步就开始爬羞羞的视频,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,等着我
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值