【问题标题】:How to write data in a node.js stream without duplicates?如何在没有重复的node.js流中写入数据?
【发布时间】:2016-04-17 16:16:07
【问题描述】:

这个问题是关于 node.js 中的 URL 爬虫的。 在start_url URL 上,他查找链接并将它们“推送”到 .json 文件 (output.json)。

我如何确保他不会两次“推送”或“写入”域到 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 对象中,然后在写入新域之前检查Set 是否已经存储。
  • 为什么不在函数末尾清除 output.json 中的重复项?
  • @nicandris 为什么首先存储重复项?这个想法真的没有效率
  • @charlietfl 如果只有几百个链接,我看不出效率问题
  • @jfriend00:我也有同样的想法。可以举个例子吗?

标签: javascript json node.js web-crawler


【解决方案1】:

您可以像这样在 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 对象是。这将确保即使只是大小写不同的域也被识别为同一个域。

【讨论】:

  • 谢谢!在某些网站上一段时间后,我在控制台中收到此错误:if (url.indexOf("://") > -1) { TypeError: Cannot read property indexOf of undefined。难道没有其他方法可以从 URL 中获取域吗?
  • @MaximilianFuchs - 为什么不使用 node.js built-in url module 来解析 URL,但该特定错误可能意味着您需要对从 <a> 标签中提取的 url 进行一些错误检查,因为它可能不存在。在继续之前,您可能需要if (link) 检查。我已将该支票添加到我的答案中。
  • 我已经尽力了,但你能告诉我为什么这不起作用吗?:url.parse(link).hostname;$("a").each(function() {之后
  • @MaximilianFuchs - “不起作用”不足以描述您的问题,无法知道如何继续。你做了什么基本的调试?究竟发生了什么?你看到了什么错误?你向控制台输出了什么?只有当您提供适当的信息并且您应该先进行自己的基本调试时,我们才能在这里提供帮助,然后,如果您仍然卡住,您应该分享您在自己的调试中所做和尝试的一切。
  • @MaximilianFuchs - url 变量名有冲突。它既是 url 模块又是函数参数名称 - 您需要更改其中一个的名称。如果您需要进一步的帮助,我建议您在新问题中发布您正在使用的实际代码,因为这个问题已经完成。
猜你喜欢
  • 1970-01-01
  • 2013-01-16
  • 2014-05-12
  • 2023-04-05
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 2018-06-24
  • 2012-09-05
相关资源
最近更新 更多