如何在 node.js 流中写入数据而不重复?

How to write data in a node.js stream without duplicates?

这个问题是关于 node.js 中的 URL-爬虫的。 在 start_url URL 上,他寻找链接并将它们 "pushes" 到 .json 文件 (output.json).

我如何确保他不会 "push" 或 "write" 域两次到 output.json (这样我就不会重复)?我一直在使用散列函数,但这引起了问题。

var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');

var start_url = ["http://blog.codinghorror.com/"]
var wstream = fs.createWriteStream("output.json");

// Extract root domain name from string
function extractDomain(url) {
    var domain;
    if (url.indexOf("://") > -1) { //find & remove protocol (http(s), ftp, etc.) and get domain
        domain = url.split('/')[2];
    } else {
        domain = url.split('/')[0];
    }
    domain = domain.split(':')[0]; //find & remove port number
    return domain;
}

var req = function(url){
    request(url, function(error, response, html){
      if(!error){
        var $ = cheerio.load(html);

        $("a").each(function() {
            var link = $(this).attr("href");
            var makelinkplain = extractDomain(link);

            start_url.push("http://" + makelinkplain);
            wstream.write('"http://'+ makelinkplain + '",');
        });
      }

        start_url.shift();

        if(start_url.length > 0) {
          return req(start_url[0]);
        }

          wstream.end();
    });
}

req(start_url[0]);

您可以像这样在 Set 对象中跟踪以前看到的域:

var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');

var domainList = new Set();
var start_url = ["http://blog.codinghorror.com/"]
var wstream = fs.createWriteStream("output.json");

// Extract root domain name from string
function extractDomain(url) {
    var domain;
    if (url.indexOf("://") > -1) { //find & remove protocol (http(s), ftp, etc.) and get domain
        domain = url.split('/')[2];
    } else {
        domain = url.split('/')[0];
    }
    domain = domain.split(':')[0]; //find & remove port number
    // since domains are not case sensitive, canonicalize it by going to lowercase
    return domain.toLowerCase();
}

var req = function(url){
    request(url, function(error, response, html){
      if(!error){
        var $ = cheerio.load(html);

        $("a").each(function() {
            var link = $(this).attr("href");
            if (link) {
                var makelinkplain = extractDomain(link);
                // see if we've already done this domain
                if (!domainList.has(makelinkplain)) {
                    domainList.add(makelinkplain);
                    start_url.push("http://" + makelinkplain);
                    wstream.write('"http://'+ makelinkplain + '",');
                }
            }
        });
      }

        start_url.shift();

        if(start_url.length > 0) {
          return req(start_url[0]);
        }

          wstream.end();
    });
}

req(start_url[0]);

注意:我还在 extractDomain() 函数中添加了 .toLowerCase(),因为域不区分大小写,但 Set 对象区分大小写。这将确保即使只是大小写不同的域也被识别为相同的域。