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、预告
既然羞羞的图片爬完了,那下一步就开始爬羞羞的视频,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,等着我