最近想写一个vue3.0的电商demo,但是苦于缺少海量的数据,于是便百度了很久,最终用node做出一个简易的爬虫。
一、首先整理下这次用到的模块
- https
- request
- fs
- path
- cheerio (https和request都选用的原因,纯粹是想都学习一点)
二、选择需要爬取的网站
let url = "https://www.XXXXXXXX.com/";
三、爬取并分析数据
https
.get(url, (res) => {
//安全判断
const { statusCode } = res; //状态码
const contentType = res.headers["content-type"]; //文件类型
let error;
//判断响应码是否为200,文件类型是否为网页
if (statusCode !== 200) {
error = new Error("Request Failed.\n" + `Status Code: ${statusCode}`);
} else if (!/^text\/html/.test(contentType)) {
error = new Error(
"Invalid content-type.\n" +
`Expected application/json but received ${contentType}`
);
}
if (error) {
console.error(error.message);
// 消费响应数据以释放内存
res.resume();
return;
}
/* 上面照抄就好 */
let rawData = "";
res.on("data", (chunk) => {
//因为数据流足够大的时候,是分段传输的,所以要拼接
rawData += chunk;
});
res.on("end", (err) => {
if (err) throw err;
//将获取到的数据写入文件,找不到文件会自动创建
fs.writeFileSync("./rawData.html", rawData);//这里就已经得到整个页面了
//用cherrio模块过滤页面数据
//1.首页商品分类图片及title
let $ = cheerio.load(rawData);
let classify = new Array();
let obj = new Object();
//遍历选中的元素节点
//因为我想要得到一个双层的json数组对象,所以这里稍微处理的有点麻烦,要遍历两次,第一层遍历到商品种类的名称,第二层在遍历到这个种类下细分的分支名称和图片。
$("div.nav-classify-item").each((index, el) => {
//定位到每个商品的具体种类
$(el)
.find("div.nav-classify-title>a")
.each((childIndex, childEl) => {
obj = {};
obj.classifyName = $(childEl).text();
//获取每种商品下的title和img
let details = new Array();
let childObj = new Object();
$(el)
.find("div.nav-classify-list-item")
.each((secondIndex, secondItem) => {
childObj = {};
childObj.img = $(secondItem).find("img").attr("data-image");
//数据中的“/”需要注意,用来命名文件时,是不支持的,需要用正则替换掉
childObj.title = $(secondItem)
.find("span")
.text()
.replace(/\//g, "-");
details.push(childObj);
});
obj.details = details;
classify.push(obj);
});
});
//写入文件
fs.writeFileSync("./homeClassify.json", JSON.stringify(classify));
});
})
.on("error", (err) => {
console.log(err.message);
});
四、等待写入完毕、调取下载函数
//5秒后执行,文件读取,不然还没写入完就读取会报错!!
setTimeout(() => {
downLoad();
}, 5000);
一定要等待写入完成以后再去读取数据!不然会报错,我这里暂时就用个定时器控制下,达到顺序执行的目的,有好的方法的帮忙指导下。
五、定义下载函数
function downLoad() {
let state = fs.existsSync(path.join("./", "classifyImgFile"));
//2.1.1如果返回true,说明文件目录存在
if (state) {
console.log("目录已存在!");
} else {
//2.1.2否则说明不存在,可以创建
fs.mkdirSync("./classifyImgFile");
console.log("目录创建完毕!");
//2,2读取文件数据
let result = JSON.parse(fs.readFileSync("./homeClassify.json").toString());
console.log("读取需要下载图片文件的数据!");
for (let itm of result) {
//获取到每个商品种类的名称,并创建文件夹
let name = itm.classifyName;
fs.mkdirSync("./classifyImgFile/" + name);
//2.3下载图片
itm.details.forEach((value, index) => {
request(value.img).pipe(
fs.createWriteStream(`./classifyImgFile/${name}/${value.title}.png`)
);
});
}
console.log("商品图片种类-子级目录创建完毕!");
}
}
最终看下成果: