2016-04-17 47 views
0

這個問題是關於node.js中的URL爬蟲。 在start_url URL中,他查找鏈接並將它們「推送」到.json文件(output.json)。如何在不重複的情況下在node.js流中寫入數據?

我怎樣才能確保他不會「推」或「寫」域兩次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]); 
+1

您可以將每個域存儲在'Set'對象中,然後在您寫入新對象之前只檢查'Set'來查看它是否已經存儲。 – jfriend00

+0

爲什麼不在函數的最後清除output.json中的重複項? – nicandris

+0

@nicandris爲什麼要在第一個地方存儲重複?那個想法真的效率不高 – charlietfl

回答

2

你可以只跟蹤先前看到的域在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]); 

注:我還添加了.toLowerCase()以來域extractDomain()功能不區分大小寫,但Set對象是。這將確保即使僅在大小寫不同的域被識別爲相同的域。

+0

謝謝!在某些網站上經過一段時間後,我在控制檯中得到這個錯誤:'if(url.indexOf(「://」)> -1){'TypeError:無法讀取未定義的屬性'indexOf'。沒有其他方式從網址獲取域名? –

+1

@MaximilianFuchs - 爲什麼不使用[node.js內置url模塊](https://nodejs.org/api/url.html#url_url_parsing)解析URL,但該特定的錯誤可能意味着您需要一些錯誤檢查從''標籤中拉出的'url',因爲它可能不存在。在繼續之前,您可能需要一個「if(link)」檢查。我已經將該檢查添加到了我的答案中。 – jfriend00

+0

我盡了我最大的努力,但是能否告訴我爲什麼這樣做不起作用?:'url.parse(link).hostname;'after'$(「a」)。'each(function(){' –

相關問題