2016-08-04 8 views
0

Node.jsが新しく、3つのURLの配列をループすることによってデータを削り取ろうとしています。スクレイプされたデータは、mongodbコレクションに格納されます。node.jsのリクエストメソッド内にデータが格納されている配列からデータにアクセスするにはどうすればよいですか?

今、urlの配列をループし、ノードのリクエストモジュールをforループ内で各URLに使用し、productsという配列にデータを動的に格納しています。

私の問題は、要求メソッドの外でコンソールにproducts.lengthを印刷しようとすると、値が0で空の配列を示すことです。ここに私のコードの一部です:

//these arrays will store the scraped information from webpage 
var prodList = []; 
var priceList = []; 

//this is the array that will be used to organize and display the scraped info 
var products = []; 

//store scraped data as an object 
function Prod(prodName, price) { 
    this.prodName = prodName; 
    this.price = price; 
}; 

var populateArray = function() { 

    //urls to scrape 
    var nyxLinks = [ 
     "http://www.nyxcosmetics.ca/en_CA/face?sz=999&viewall=1", 
     "http://www.nyxcosmetics.ca/en_CA/lips?sz=999&viewall=1", 
     "http://www.nyxcosmetics.ca/en_CA/eyes?sz=999&viewall=1" 
    ]; 

    //empty all arrays 
    prodList = []; 
    priceList = []; 
    products = []; 

    for(var i = 0; i < nyxLinks.length; i++) { 

     //define url to download 
     var url = nyxLinks[i]; 
     console.log(url); 

     request(url, function(error, response, body) { 
      if(!error) { 

       //load page into cheerio 
       var $ = cheerio.load(body); 

       //for each product on the page store in respective arrays 
       $(".product_tile_wrapper").each(function(i, elem) { 
        prodList.push($(this).find($(".product_name")).attr("title")); 
        priceList.push($(this).find($(".product_price")).attr("data-pricevalue")); 
       }); 

       for(var i = 0; i < prodList.length; i++) { 
        //store product info as an object 

        products.push(new Prod(prodList[i], priceList[i])); 
       } 
      } else { 
       console.log("We've encountered an error!") 
      } 
     }).on("end", function(err, data) {   
      if(!err) { 
       console.log("products length " + products.length); 
      } else { 
       console.log(err); 
      } 
     }); 
    } 
    console.log("products length " + products.length); 
} 

mongoose.connect('mongodb://127.0.0.1:27017/makeupdb'); 

var db = mongoose.connection; 
db.on('error', console.error.bind(console, 'Connection Error:')); 
db.once('open', function() { 
    // we're connected 

    populateArray(); 
    console.log("number of products in products array " + products.length); 

    //clear the current collection - db.remove({}) 

    //insert data in mongodb - db.insert(products) 

}); 

このコードからコンソール出力は次のとおりです。

Server running at http://127.0.0.01:1337/ 
http://www.nyxcosmetics.ca/en_CA/face?sz=999&viewall=1 
http://www.nyxcosmetics.ca/en_CA/lips?sz=999&viewall=1 
http://www.nyxcosmetics.ca/en_CA/eyes?sz=999&viewall=1 
products length 0 
number of products in products array 0 
products length 0 
products length 31 
products length 119 

私は製品のアレイにアクセスできるようにするには、コールバックを使用する必要があるが、私は確認していないと信じて私はこのコールバックを使用する必要があります。どんな助けでも大歓迎です。

おかげで、

ラダ

答えて

2

これが原因ではJavaScriptの非同期モデルです。エンジンは.on('end')コールバックが実行されるのを待ってから、forループの次の繰り返しに移ります。

コールバックが呼び出されるたびに増分され、カウンターが要求の数に達するとカウンター変数を持つことができます。最後の関数を呼び出します。このような何か:

var numRequestsFinished = 0; 
var products = []; 
var finalCallback = function() { 
    console.log('Final Products:', products); 
}; 
for (var i = 0; i < nyxLinks.length; i++) { 
    request(..., function(err, data) { 
    numRequestsFinished++; 
    // error checking 

    products.push(data); 

    if (numRequestsFinished === nyxLinks.length) { 
     finalCallback(); 
    } 
    }); 
} 

代わりに、あなたは約束ライブラリなどBluebirdを見て、Promise.all APIを取ることができます。これにより、配列内のすべての約束事が完了したときに呼び出される関数を定義することができます。

関連する問題