[nodejs实战:校花网学妹图片采集+站点发布]五、批量采集:异步操作async模块

上一篇的批量采集其实只是一个开头,是为了批量采集而搜集大量的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模块

上一篇:

下一篇: