!ta竟然用Node.js對這些beauty圖做了這些事。。。

!ta竟然用Node.js對這些beauty圖做了這些事。。。

做了什麼

一個用於爬取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

斷線繼續下載

圖片下載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檔案
分析相簿地址,以韓國

封裝獲取指定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

感謝

本文有參考nieheyongHanhandeSpider和其他的爬蟲文章,得到很多啟發