2016-07-25 64 views
0

我正在寫一個內容刮板,用於刮取特定網站上襯衫的信息。我已經在Node中使用NPM軟件包設置了一切,以便創建CSV文件。我遇到的問題是,很多人都知道,Node本質上是異步的。我試圖編寫的CSV文件是在我創建的JSON對象完成創建之前編寫的(迭代每個循環來構建它),因此它傳遞給json2csv(npm包)的'fields'參數。但它將我的數據作爲空對象傳遞。任何人都可以告訴我如何告訴節點,直到我的json對象生成之前,試圖使用fs.writefile來創建CSV文件?謝謝四處逛逛Node的異步性

'use strict'; 

//require NPM packages 

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

//Array for shirts JSON object for json2csv to write. 
var ShirtProps = []; 

var homeURL = "http://www.shirts4mike.com/"; 

//start the scraper 
scraper(); 

//Initial scrape of the shirts link from the home page 
function scraper() { 
    //use the datafolderexists function to check if data is a directory 
    if (!DataFolderExists('data')) { 
    fs.mkdir('data'); 
    } 
    //initial request of the home url + the shirts.php link 
    request(homeURL + "shirts.php", function (error, response, html) { 
    if (!error && response.statusCode == 200) { 
     var $ = cheerio.load(html); 

     //scrape each of the links for its html data 
     $('ul.products li').each(function(i, element){ 
     var ShirtURL = $(this).find('a').attr('href'); 
     console.log(ShirtURL); 
     //pass in each shirtURL data to be scraped to add it to an object 
     ShirtHTMLScraper(ShirtURL); 
     }); 
     FileWrite(); 
     // end first request 
    } else { 
     console.error(error); 
    } 
    }); 
} 

//create function to write the CSV file. 
function FileWrite() { 
    var fields = ['Title', 'Price', 'ImageURL', 'URL', 'Time']; 
    var csv = json2csv({data: ShirtProps, fields: fields}); 
    console.log(csv); 
    var d = new Date(); 
    var month = d.getMonth()+1; 
    var day = d.getDate(); 
    var output = d.getFullYear() + '-' + 
    ((''+month).length<2 ? '0' : '') + month + '-' + 
    ((''+day).length<2 ? '0' : '') + day; 

    fs.writeFile('./data/' + output + '.csv', csv, function (error) { 
    if (error) throw error;  
    });  
} 

//function to scrape each of the shirt links and create a shirtdata object for each. 
function ShirtHTMLScraper(ShirtURL) { 
    request(homeURL + ShirtURL, function (error, response, html) { 
    if (!error && response.statusCode == 200) { 
     var $ = cheerio.load(html); 
     var time = new Date().toJSON().substring(0,19).replace('T',' '); 
     //json array for json2csv 
     var ShirtData = { 
     title: $('title').html(), 
     price: $(".price").html(), 
     imgURL: $('img').attr('src'), 
     url: homeURL + ShirtURL, 
     time: time.toString() 
     }; 
     //push the shirt data scraped into the shirtprops array 
     ShirtProps.push(ShirtData); 
     console.log(ShirtProps); 

     // //set the feilds in order for the CSV file 
     // var fields = ['Title', 'Price', 'ImageURL', 'URL', 'Time']; 

     // //use json2csv to write the file - 

     // var csv = json2csv({data: ShirtProps, fields: fields}); 
     // console.log(csv); 

     // //date for the filesystem to save the scrape with today's date. 
     // var d = new Date(); 
     // var month = d.getMonth()+1; 
     // var day = d.getDate(); 
     // var output = d.getFullYear() + '-' + 
     // ((''+month).length<2 ? '0' : '') + month + '-' + 
     // ((''+day).length<2 ? '0' : '') + day; 

     // //use filesystem to write the file, or overrite if it exists. 
     //  fs.writeFile('./data/' + output + '.csv', csv, function (error) { 
     //  if (error) throw error; 

     //  }); //end writeFile 
    } else { 
     console.error(error); 
    } 
    }); 
} 

//Check if data folder exists, source: http://stackoverflow.com/questions/4482686/check-synchronously-if-file-directory-exists-in-node-js 
function DataFolderExists(folder) { 
    try { 
    // Query the entry 
    var DataFolder = fs.lstatSync(folder); 

    // Is it a directory? 
    if (DataFolder.isDirectory()) { 
     return true; 
    } else { 
     return false; 
    } 
    } //end try 
    catch (error) { 
    console.error(error); 
    } 
} 
+3

擁抱正確的javascript技術的異步性質,而不是與他們戰鬥 –

回答

3

這不是這麼多節點在本質上是異步的,因爲它是某些職能是異步的。在這種情況下,使用請求的調用是異步的。您在第二個請求調用(ShirtHTMLScraper中的一個)開始之後直接調用FileWrite。在填充ShirtProps之後,將呼叫放置在ShirtHTMLScraper回調中的FileWrite。

編輯:在仔細觀察後,這也行不通。問題是你正在同步循環內調用一個異步函數。您可以通過創建一個計數器來增加每個異步回調的工作量,並檢查您是否擊中了正在迭代的項目的長度。如果您正在進行最後一次迭代,請運行FileWrite。

更好的方法可能是檢查異步庫。您可以使用.each()提供兩個回調,一個在每次迭代中運行,一個在完成時運行。

+0

馬特,我試過這個,它仍然無法正常工作,它顯示空白數據的每個時間調用filewrite,在我設置了正確的字段下。另外,如果我將它放在ShirtHTMLScraper的回調函數中,它會在每次遍歷每個襯衫鏈接時寫入該文件,我希望它在填充包含ShirtProps對象的鍵值對的對象後再寫一次文件。 – BrokenWings

+0

推空之後ShirtProps的console.log是什麼?因爲如果它是正確的,並且FileWrite在它後面調用時失敗,則FileWrite存在問題。我明白了你對重複寫入的意義,我也會看到這一點 –

+0

不,ShirtProps的console.log很好,並且實際上顯示它在嘗試寫入文件之後構建對象,因爲console.log被每次調用它會迭代襯衫鏈接並抓取數據,我這樣做是爲了讓我可以一次看到實際上正在構建的襯衫1件襯衫,但是當它試圖寫入文件後,對象完成構建:( – BrokenWings