资讯专栏INFORMATION COLUMN

!ta竟然用Node.js对这些beauty图做了这些事。。。

mayaohua / 2669人阅读

摘要:做了什么一个用于爬取上妹子图片的爬虫。于是观察浏览器正常浏览行为。在请求头中设置和。解决该问题断线继续下载图片下载个文件时,经常断线。应该是网站的饭爬虫机制起了作用,暂时无法解决。于是在保存图片时会先判断图片是否存在。

做了什么

一个用于爬取www.nvshens.com上妹子图片的爬虫。如有侵权,马上关闭

原因

一张张下实在太麻烦了

如何使用
0. node -v >= 7.6
1. git clone https://github.com/laihaibo/beauty-spider.git
2. npm i
3. npm run start (爬取相册图片链接,并保存为json)
4. npm run calc (获取爬取的相册数和文件数)
5. npm run download (下载图片文件)
update against反爬虫

图片下载完之后会发现变成了盗链图片。于是观察浏览器正常浏览行为。在请求头中设置referer, acceptuser-agent。解决该问题

request.get(url).set({
        "Referer": "https://www.google.com",
        "Accept": "image/webp,image/*,*/*;q=0.8",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3091.0 Safari/537.36"
      }).end((err, res) => {})
断线继续下载

图片下载700个文件时,经常断线。应该是网站的饭爬虫机制起了作用,暂时无法解决。重新下载时理应跳过已经下载的文件。于是在保存图片时会先判断图片是否存在。

let isExit = fs.existsSync(path);
if (!isExit) {
  saveOne(...args)
}
获取理应下载的相册数和文件数
let data = JSON.parse(fs.readFileSync(path));

let count = data.reduce((prev, cur) => prev + cur.imgList.length, 0);

console.log(`共${data.length}个相册,共${count}张图片`);
步骤

引入所需的库

const fs = require("fs");
const mkdirp = require("mkdirp");
const cheerio = require("cheerio");
const request = require("superagent");
require("superagent-charset")(request);

页面分析,配置config文件
分析相册地址,以韩国这个标签为例,首页为https://www.nvshens.com/gallery/hanguo/, 第二页为https://www.nvshens.com/gallery/hanguo/2.html

const config = {
  current: "hanguo",
  allTags: {
    rougan: `https://www.nvshens.com/gallery/rougan/`,
    hanguo: "https://www.nvshens.com/gallery/hanguo/"
  }
}

封装获取指定url的html内容函数

//该网站编码为utf-8
const getHtml = url => {
  return new Promise((resolve, reject) => {
    request.get(url).charset("utf-8").end((err, res) => {
      err ? reject(err) : resolve(cheerio.load(res.text));
    })
  })
}

获取本分类下所有相册的标签

/**
 * @param {string} startUrl 标签首页的url地址
 */
const getAlbums = (startUrl) => {
  return new Promise((resolve, reject) => {
    let albums = [];  // 用于保存该标签的所有相册信息

    let getQuery = async startUrl => {
      try {
        let $ = await getHtml(startUrl);
        let pages = $("#listdiv .pagesYY a").length;  // 获取页数

        for (let i = 1; i <= pages; i++) {
          let pageUrl = `${startUrl + i}.html`  // 设置每页的url
          let $ = await getHtml(pageUrl);

          // 动态设置pages的值
          let compare = $("#listdiv .pagesYY a").map(function (i, el) {
            return parseInt($(this).text(), 0);
          }).get().filter(x => x > 0);
          pages = conmpare.length < 2 ? pages : compare.reduce((prev, cur) => Math.max(prev, cur));

          $(".galleryli_title a").each(function () {
            albums.push({
              title: $(this).text(),
              url: `https://www.nvshens.com${$(this).attr("href")}`,
              imgList: [],
              id: parseInt($(this).attr("href").split("/")[2], 10)
            })
          })
        }

        resolve(albums);  // 返回相册信息
      } catch (error) {
        console.log(error);
      }
    }

    getQuery(startUrl);
  })
}

获取所有相册的图片信息

/**
 * @param {string} startUrl 该相册首页的url地址
 */
const getImgList = (startUrl) => {
  return new Promise((resolve, reject) => {
    let albums = [];  // 存储本相册的所有图片信息

    let getQuery = async startUrl => {
      try {
        let $ = await getHtml(startUrl);
        let pages = $("#pages a").length;

        for (let i = 1; i <= pages; i++) {
          let pageUrl = `${startUrl + i}.html`
          let $ = await getHtml(pageUrl);

          $("#hgallery img").each(function () {

            let url = $(this).attr("src");  //图片地址
            let fileName = url.split("/").pop();  //文件名
            let id = parseInt(fileName.split(".")[0], 10); //id

            albums.push({
              url,
              fileName,
              id
            })
          })
        }

        resolve(albums); // 返回本相册的所有图片信息
      } catch (error) {
        console.log(error);
      }
    }

    getQuery(startUrl);
  })
}

保存相册信息

/**
 * @param {string} path 保存数据的路径
 * @param {array} albums 相册信息数组
 */
const saveData = (path, albums) => {
    fs.writeFile(path, JSON.stringify(albums, null, " "), function (err) {
        err ? console.log(err) : console.log("Data saved");
    });
}

保存图片

/**
 12. @param {string} title 图片所在文件夹名
 13. @param {string} url 图片url
 14. @param {string} fileName 图片名
 15. @param {array} imgList 单个相册的图片信息
 */
// 保存一张图片
const saveOne = (title, url, fileName) => {
  return new Promise((resolve, reject) => {
    let path = `./img/${currentImgType}/${title}/${fileName}`;
    request.get(url).end((err, res) => {
      if (err) {
        console.log(`Error: ${err} in getting ${url}`)
      }
      fs.writeFile(path, res.body, function (err) {
        if (err) console.log(`Error: ${err} in downloading ${url}`)
      });
      resolve();
    })
  })
}

//保存一个相册下的多张图片
const saveImg = ({title,imgList}) => {
  // 创建文件夹
  mkdirp(`./img/${currentImgType}/${title}`, function (err) {
    if (err) {
      console.log(`Error: ${err} in makedir ${title}`);
    }
  });

  let getQuery = async() => {
    try {
      for (let {url,fileName} of imgList) {
        await saveOne(title, url, fileName);
      }
    } catch (error) {
      console.log(error);
    }
  }

  // 打印下载一个相册所需时间
  console.time(`download ${title}...`)
  getQuery();
  console.timeEnd(`download ${title}...`)
}

执行爬虫

const doSpider = async() => {
  try {
    // 获取相册信息
    let albums = await getAlbums(allTags[current]);

    // 获取每张图片信息
    for (let album of albums) {
      let imgList = await getImgList(album.url);
      album.imgList = imgList;
    }

    // 保存json
    let jsonPath = `./data`;
    mkdirp(jsonPath, function (err) {
      if (err) {
        console.log(`Error: ${err} in makedir of Json`);
      }
    });
    saveData(`${jsonPath}/${currentImgType}.json`, albums);

    // 保存图片
    for (let value of albums) {
      saveImg(value)
    }

  } catch (error) {
    console.log(error);
  }
}

心得体会

有些坑如果不踩过一遍是不会吐血的,比如cheerio的操作和fs的操作

just do it

感谢

本文有参考nieheyong的HanhandeSpider和其他的爬虫文章,得到很多启发

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84186.html

相关文章

  • 谷歌3亿张做了个深度学习实验,结论:数据还是越大越好

    摘要:然而,可用数据集的规模却没有成比例地扩大。这还说明无监督表征学习,以及半监督表征学习方法有良好的前景。例如,对于对象探测得分,单个模型目前可以实现,高于此前的。此外,构建包含图片的数据集并不是最终目标。 都说深度学习的兴起和大数据息息相关,那么是不是数据集越大,训练出的图像识别算法准确率就越高呢?Google的研究人员用3亿张图的内部数据集做了实验,然后写了篇论文。他们指出,在深度模型中,视...

    twohappy 评论0 收藏0
  • 前端每周清单:Node.js 微服务实践,Vue.js 与 GraphQL,Angular 组件技巧

    摘要:前端每周清单第期微服务实践,与,组件技巧,攻防作者王下邀月熊编辑徐川前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。 前端每周清单第 26 期:Node.js 微服务实践,Vue.js 与 GraphQL,Angular 组件技巧,HeadlessChrome 攻防 作者:王下邀月熊 编辑:徐川...

    wall2flower 评论0 收藏0
  • 浅析webpack源码之convert-argv模块(二)

    摘要:接下来我看看一下函数我们先按照分支走为读取是里的对象,饶了这大的一个圈子,那么接下来一起来看一看对你的输入配置做了怎么样的处理吧 打开webpeck-cli下的convert-argv.js文件 // 定义options为空数组 const options = []; // webpack -d 检查 -d指令 if (argv.d) { //... } ...

    lemon 评论0 收藏0
  • 精读《深入浅出Node.js

    摘要:从社区和过往的经验而言异步编程的难题已经基本解决无论是通过事件还是通过模式或者流程控制库。本章主要介绍了主流的几种异步编程解决方案这是目前中主要使用的方案。最后因为人们总是习惯性地以线性的方式进行思考以致异步编程相对较为难以掌握。 前言 如果你想要深入学习Node,那你不能错过《深入浅出Node.js》这本书,它从不同的视角介绍了 Node 内在的特点和结构。由首章Node 介绍为索引...

    codergarden 评论0 收藏0
  • Android项目刮刮奖详解扩展篇——开源刮刮奖View的制作

    摘要:项目刮刮奖详解四前言我们已经成功实现了刮刮奖的功能了,本期是扩展篇,我们把这个直接定义成开源控件,发布到上,以后有需要也可以直接使用,关于自定义控件的知识,不了解的同学可以看这下面我之前写的这两篇自定义控件开发发布第三方库到上实现定义属性文Android项目刮刮奖详解(四) 前言 我们已经成功实现了刮刮奖的功能了,本期是扩展篇,我们把这个View直接定义成开源控件,发布到JitPack上,以...

    Tychio 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<