Node.js Request+Cheerio实现一个小爬虫-基础功能实现1:内容抓取
Node.js Request+Cheerio实现一个小爬虫-基础功能实现2:文件写入
Node.js Request+Cheerio实现一个小爬虫-基础功能实现3:流程控制及并发控制
Node.js Request+Cheerio实现一个小爬虫-番外篇:代理设置
上一篇中虽然成功抓下数据了,但是在写入文件中去的时候却发生了问题。我们抓到的数据并没有写到文件里去。其实是因为Node采用了大量的异步操作,对于一些比较花时间的任务并不会按照我们写的顺序执行。对于后续的任务一般都是采用回调函数的方式来执行。这一部分可以参照阮一峰老师的文章JavaScript 标准参考教程(alpha)中Node.js一部分。
那么就来改一下代码,把文件写入的操作塞到request函数里面去。
const request = require('request');
const cheerio = require('cheerio');
const fs = require('fs');
// url 和头部的设定省略
var shopLists = [];
request(option, function(error, response, body) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(body, {
ignoreWhitespace: true,
xmlMode: true
});
var shopInfo = {
pageNo: option.url.match(/g\d+p(\d+)/)[1],
pageURL: option.url,
info: []
};
var shopList = $('div#shop-all-list').find('a[data-hippo-type = "shop"]');
shopList.each(function(no, shop) {
let info = {};
info.no = no + 1;
info.name = $(shop).attr('title');
info.url = $(shop).attr('href');
shopInfo.info.push(info);
});
shopLists.push(shopInfo);
// 在这里加入写进文件的函数
fs.writeFile(FILE_PATH + FILE_NAME, shopLists 'utf-8', function(err) {
if (err) {
console.error("文件生成时发生错误.");
throw err;
}
console.info('文件已经成功生成.');
});
}
});
好了,这样一来就能把想要的shopLists
的内容写入到文件中去了。但是用writeFile方法的话,在要写入多条数据的情况下,每次写入数据时都是覆盖而不是追加。所以多条数据的情况下,实际上最后只会写入一条数据。这个问题只要稍稍修改一下文件写入的方法就行了。
// 将原来的write方向改为append方法
// 判断一下文件是否已经存在,如果存在那么就追加,否则就先生成一个文件。
fs.exists(FILE_PATH + FILE_NAME, function(exits) {
if (exits) {
fs.appendFile(FILE_PATH + FILE_NAME, shopLists 'utf-8', function(err) {
if (err) {
console.error("文件生成时发生错误.");
throw err;
}
});
} else {
console.info('文件不存在,将生成新文件.');
// 对于写入的内容shopLists,最好可以用JSON.stringify转化一下。
//如果把数组直接写入文件的话,很可能会得到 [Object] 这样的形式
fs.writeFile(FILE_PATH + FILE_NAME, shopLists 'utf-8', function(err) {
if (err) {
console.error("文件生成时发生错误.");
throw err;
}
console.info('文件已经成功生成.');
});
}
});
那么这样一来我们要的内容就能顺利被写到文件中去了。就像前面说的,问题是解决一个又来一个的。因为Node异步执行的原因,在有多条请求的时候,我们是不知道哪条请求会先执行完。以上面的代码为例,虽然我们可能是按照1,2,3的顺序传入的url来发起请求,但实际上可能得到的是3,2,1这样的结果。那么还是以上面作为例子。我们可以对shopLists
的长度进行判断,当shopLists
的长度达到了我们的要求,对其排序后写到文件中去。(相当于设置了一个计数器)
// 设定一个目标长度
const DATA_LENGTH = 50;
// 省略的部分与前面相同
request(option, function(error, response, body) {
// 省略的部分与前面相同
shopLists.push(shopInfo);
// 每次push之后对长度进行一次检查
doWirteFile(shopLists);
}
});
function doWirteFile(shopLists, DATA_LENGTH){
if(shopLists.length === DATA_LENGTH){
// 执行文件写入操作
}
}
这是一个比较简单的方法。当然也可以使用emit方法对每次push进行监听也能达到同样的效果。但是如果我们不知道需要的数组的长度的话,那么这样的方法就不太适用了。对于这样的情况就留到下次再说吧。
网友评论