上一篇的批量采集其实只是一个开头,是为了批量采集而搜集大量的url,而本篇文章才是真正的批量采集。
上一篇已经把校花网搜索校花的页面url存入了urlList.txt文件中,在批量采集前需要先读取这个文件:
fs.readFile('urlList.txt', function (err, data) { if (err) { return false; } else { var urls = data.toString();//将读取的内容转换成字符串 var urlsArr=urls.split(',');//将字符串转换成数组,便于遍历 console.log(urlsArr); } });
接下来大家的思路可能是直接for循环遍历数组urlsArr,然后调用上一篇封装好的spider模块来采集数据:
var http = require('http'); var fs = require('fs'); var spider = require('./spider'); fs.readFile('urlList.txt', function(err, data) { if (err) { return false; } else { var urls = data.toString(); //将读取的内容转换成字符串 var urlsArr = urls.split(','); //将字符串转换成数组,便于遍历 for (var i in urlsArr) { spider(urlsArr[i], null, function(data) { console.log(data) }); } } });
以上代码可以保存为getDetails.js文件,执行过程发现会报各种错误,同时又会console.log出很多结果,导致的原因很简单:for循环会瞬间发出大量请求,导致对方服务器受不了压力而直接拒绝。所以大批量采集数据的时候要考虑到并发的处理,同理,其他的大量的异步操作也要考虑并发的处理。
nodejs处理异步操作的方式有很多,比如Async、Promise等。本教程采取Async。Async用法参考:http://blog.fens.me/nodejs-async/
var http = require('http'); var fs = require('fs'); var async = require('async'); var spider = require('./spider'); fs.readFile('urlList.txt', function(err, data) { if (err) { return false; } else { var urls = data.toString(); //将读取的内容转换成字符串 var urlsArr = urls.split(','); //将字符串转换成数组,便于遍历 async.eachLimit(urlsArr, 1, function(file, callback) { spider(file, null, function(data) { cosole.log(data); callback(); //必须添加,不然没办法走下一流程 }); }, function(err, result) { console.log('All Success'); }); } });
这样便控制了并发数。接下来要做的就是跟第三篇“数据的筛选:cheerio模块”那一章要做的一样了,提取核心内容,然后保存成对应的html文件。
不过有些地方可以稍微改进一下。因为是批量进行采集,有时候即使控制并发数也不能保证对方服务器不卡死,所以我们免不了需要多次执行getDetails.js文件,但是有些已经采集成功的页面没必要再重新采集,所以可以在采集之前判断本地有没有保存该页面的html文件,如果有,则跳过,没有则采集。
判断文件是否存在则需要fs模块的exists方法。
fs.exists(filename, function(stas) { if (stas) { console.log("存在"); } else { fs.writeFile(fileName, data, function() { console.log("不存在"); }); } }); //对应的同步方法:fs.existsSync(filename);返回true 存在,false 不存在。 //不过我在几次大批量操作中感觉同步方法好像会出现问题,不如fs.exists准确。这个结论我也不确定真假,大家多测试吧
于是,一个完整的采集代码是这样的:
var http = require('http'); var cheerio = require('cheerio'); var iconv = require('iconv-lite'); var BufferHelper = require('bufferhelper'); var fs = require('fs'); var async = require('async'); var spider = require('./spider'); fs.readFile('urlList.txt', function(err, data) { if (err) { return false; } else { var urls = data.toString(); var urlArr = urls.split(','); async.eachLimit(urlArr, 1, function(eachUrl, callback) { var fileName = 'detail/' + eachUrl.replace(/http:\/\/www.xiaohuar.com\//, ''); fs.exists(fileName, function(stas) { if (stas) { console.log(fileName + "已存在"); callback(); } else { spider(eachUrl, null, function(data) { var val = iconv.decode(data.toBuffer(), 'gb2312'); var $ = cheerio.load(val, { decodeEntities: false }); //decodeEntities: false 避免乱码 var title = "<title>" + $(".wrap h1").html() + "</title>"; var contentObj = $(".content_wrap"); contentObj.find(".gg4,a").remove(); contentObj.find("p,img,span").each(function() { $(this).removeAttr("class"); $(this).removeAttr("style"); $(this).removeAttr("alt"); $(this).removeAttr("title"); $(this).removeAttr("id"); }); var contentHtml = contentObj.html(); contentHtml = contentHtml.replace(/<span>/g, ''); contentHtml = contentHtml.replace(/<\/span>/g, ''); contentHtml = contentHtml.replace(/src="\/d\/file/g, 'src="http://www.xiaohuar.com/d/file'); contentHtml = "<content>" + contentHtml + "</content>"; var mainHtml = title + contentHtml; fs.writeFile(fileName, mainHtml, function() { console.log(fileName + '==>success'); callback(); }); }); } }); }, function(err, result) { console.log('All Success'); }); } });
文末强行扣题——Async模块相关知识都在这里了:http://blog.fens.me/nodejs-async/ 我也是看的这篇文章,就没必要再把人家的内容贴过来了。。。
未经允许不得转载:前端撸码笔记 » [nodejs实战:校花网学妹图片采集+站点发布]五、批量采集:异步操作async模块