2016-09-22 3 views
3

Я создаю скребок для содержания на сайте футболки.Как я могу переписать это с обещаниями?

Цель состоит в том, чтобы войти в сайт только через один закодированный URL: http://shirts4mike.com

Затем я найти все страницы продукта для каждой футболки, а затем создать объект с его деталью. Затем добавьте его в массив.

Когда массив заполнен майками, я буду работать через массив и зарегистрировать его в CSV-файле.

Прямо сейчас у меня возникают проблемы с временем запросов/ответов и вызовами функций.

Как я могу убедиться, что я вызываю функцию NEXT в нужное время? Я понимаю, что он не работает из-за его асинхронного характера.

Как я могу позвонить secondScrape, lastScraper и convertJson2Csv в нужное время, чтобы переменные, с которыми они работают, не были определены?

Я пытался использовать что-то вроде response.end(), но это не работает.

Я предполагаю, что мне НЕОБХОДИМО использовать обещания, чтобы сделать эту работу должным образом? и быть разборчивым?

Любые идеи? Мой код ниже:

//Modules being used: 
var cheerio = require('cheerio'); 
var request = require('request'); 
var moment = require('moment'); 

//hardcoded url 
var url = 'http://shirts4mike.com/'; 

//url for tshirt pages 
var urlSet = new Set(); 

var remainder; 
var tshirtArray; 


// Load front page of shirts4mike 
request(url, function(error, response, html) { 
    if(!error && response.statusCode == 200){ 
     var $ = cheerio.load(html); 

    //iterate over links with 'shirt' 
     $("a[href*=shirt]").each(function(){ 
      var a = $(this).attr('href'); 

      //create new link 
      var scrapeLink = url + a; 

      //for each new link, go in and find out if there is a submit button. 
      //If there, add it to the set 
      request(scrapeLink, function(error,response, html){ 
       if(!error && response.statusCode == 200) { 
        var $ = cheerio.load(html); 

        //if page has a submit it must be a product page 
        if($('[type=submit]').length !== 0){ 

         //add page to set 
         urlSet.add(scrapeLink); 

        } else if(remainder === undefined) { 
         //if not a product page, add it to remainder so it another scrape can be performed. 
         remainder = scrapeLink;      
        } 
       } 
      }); 
     });  
    } 
    //call second scrape for remainder 
    secondScrape(); 
}); 


function secondScrape() { 
    request(remainder, function(error, response, html) { 
     if(!error && response.statusCode == 200){ 
      var $ = cheerio.load(html); 

      $("a[href*=shirt]").each(function(){ 
       var a = $(this).attr('href'); 

       //create new link 
       var scrapeLink = url + a; 

       request(scrapeLink, function(error,response, html){ 
        if(!error && response.statusCode == 200){ 

         var $ = cheerio.load(html); 

         //collect remaining product pages and add to set 
         if($('[type=submit]').length !== 0){ 
          urlSet.add(scrapeLink); 
         } 
        } 
       }); 
      });  
     } 
    }); 
    console.log(urlSet); 
    //call lastScraper so we can grab data from the set (product pages) 
    lastScraper(); 
}; 



function lastScraper(){ 
    //scrape set, product pages 
    for(var i = 0; i < urlSet.length; i++){ 
     var url = urlSet[i]; 

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

       //grab data and store as variables 
       var price = $('.price').text(); 
       var img = $('.shirt-picture').find("img").attr("src"); 
       var title = $('body').find(".shirt-details > h1").text().slice(4); 

       var tshirtObject = {}; 
       //add values into tshirt object 

       tshirtObject.price = price; 
       tshirtObject.img = img; 
       tshirtObject.title = title; 
       tshirtObject.url = url; 
       tshirtObject.date = moment().format('MMMM Do YYYY, h:mm:ss a'); 

       //add the object into the array of tshirts 
       tshirtArray.push(tshirtObject); 
      } 
     }); 
    } 
    //call function to iterate through tshirt objects in array in order to convert to JSON, then into CSV to be logged 
    convertJson2Csv(); 
}; 
+0

Простое преобразование в обещания все равно оставит вас с неэффективным процессом, в котором страницы будут пересмотрены. Хорошая парадигма позволит каждой странице посещать максимум один раз. –

ответ

0

Существует модуль НПМ называется request-promise.

просто:

var rp = require("request-promise"); 

и в любом месте вы делаете заявку вы можете переключиться с запросом-обещание.

, например:

rp(url) 
.then(function(value){ 
    //do whatever 
}) 
.catch(function(err){ 
    console.log(err) 
}) 
0

Вы можете использовать waterfall метод async модуля, который может дать вам гладкий путь для решения этой проблемы.

Я просто пытаюсь сделать свой код с этим модулем

Надежда это будет работать для вас

Формат водопада

async.waterfall([ 
    function(callback) { 
    callback(null, previousvalue); 
    }, 
    function(previousvalue, callback) {} 
], function(err, result) { //Final callback 

}); 

var async = require('async'); 
var cheerio = require('cheerio'); 
var request = require('request'); 
var moment = require('moment'); 

//hardcoded url 
var url = 'http://shirts4mike.com/'; 

//url for tshirt pages 
var urlSet = new Set(); 

var remainder; 
var tshirtArray = []; 


async.waterfall([ 
    function(callback) { 
    // Load front page of shirts4mike 
    request(url, function(error, response, html) { 
     if (!error && response.statusCode == 200) { 
     var $ = cheerio.load(html); 

     //iterate over links with 'shirt' 
     $("a[href*=shirt]").each(function() { 
      var a = $(this).attr('href'); 

      //create new link 
      var scrapeLink = url + a; 

      //for each new link, go in and find out if there is a submit button. 
      //If there, add it to the set 
      request(scrapeLink, function(error, response, html) { 
      if (!error && response.statusCode == 200) { 
       var $ = cheerio.load(html); 

       //if page has a submit it must be a product page 
       if ($('[type=submit]').length !== 0) { 

       //add page to set 
       urlSet.add(scrapeLink); 
       callback(null, true); 

       } else if (remainder === undefined) { 
       //if not a product page, add it to remainder so it another scrape can be performed. 
       remainder = scrapeLink; 
       callback(nul, true); 
       } 
      } 
      }); 
     }); 
     } 
     //call second scrape for remainder 
     // secondScrape(); 
    }); 
    }, 
    function(previousvalue, callback) { 
    request(remainder, function(error, response, html) { 
     if (!error && response.statusCode == 200) { 
     var $ = cheerio.load(html); 

     $("a[href*=shirt]").each(function() { 
      var a = $(this).attr('href'); 

      //create new link 
      var scrapeLink = url + a; 

      request(scrapeLink, function(error, response, html) { 
      if (!error && response.statusCode == 200) { 

       var $ = cheerio.load(html); 

       //collect remaining product pages and add to set 
       if ($('[type=submit]').length !== 0) { 
       urlSet.add(scrapeLink); 
       } 
       callback(null, true); 
      } 
      }); 
     }); 
     } 
    }); 
    console.log(urlSet); 
    //call lastScraper so we can grab data from the set (product pages) 
    }, 
    function(previousvalue, callback) { 
    //scrape set, product pages 
    for (var i = 0; i < urlSet.length; i++) { 
     var url = urlSet[i]; 

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

      //grab data and store as variables 
      var price = $('.price').text(); 
      var img = $('.shirt-picture').find("img").attr("src"); 
      var title = $('body').find(".shirt-details > h1").text().slice(4); 

      var tshirtObject = {}; 
      //add values into tshirt object 

      tshirtObject.price = price; 
      tshirtObject.img = img; 
      tshirtObject.title = title; 
      tshirtObject.url = url; 
      tshirtObject.date = moment().format('MMMM Do YYYY, h:mm:ss a'); 

      //add the object into the array of tshirts 
      tshirtArray.push(tshirtObject); 
     } 
     }); 
    } 
    } 
], function(err, result) { 
    //call function to iterate through tshirt objects in array in order to convert to JSON, then into CSV to be logged 
    convertJson2Csv(); 
}); 
0

Вы можете используйте этот пример для конвертировать остальную часть вашего образца кода.

promise = new Promise((resolve, reject) => ( 
    request("http://shirts4mike.com/", 
    (err, response, html) => (response.statusCode == 200 ? resolve(html): reject(err)) 
))); 


promise.then(html => { 
    var $ = cheerio.load(html); 
    // continue 
}); 
+0

Смогу ли я создать новое обещание для каждого отдельного запроса? – bloppit

+0

Да, это был бы путь. –

+0

Я думаю, что вы сможете реорганизовать функцию, которая получает URL-адрес, и возвращает обещание и вызывает эту функцию один раз для каждого запроса на URL-адрес. –

0

Вы правильно идентифицируете обещания как путь вперед к решению ваших проблем с сроками.

Для того, чтобы иметь обещания, вам необходимо промизировать request (или принять HTTP lib, методы которого возвращают обещания).

Вы можете просто исправить проблемы времени с обещаниями, но вы также можете воспользоваться возможностью, чтобы улучшить общую парадигму. Вместо дискретных функций для практически идентичных первых/вторых/третьих этапов вы можете написать одну функцию, которая вызывает себя рекурсивно. Правильно написано, это гарантирует, что каждая страница на целевом сайте посещается максимум один раз; следует избегать повторных посещений по причине общей производительности и загрузки целевого сервера.

//Modules being used: 
var Promise = require('path/to/bluebird'); 
var cheerio = require('cheerio'); 
var moment = require('moment'); 

// Promisify `request` to make `request.getAsync()` available. 
// Ref: http://stackoverflow.com/questions/28308131/how-do-you-properly-promisify-request 
var request = Promise.promisify(require('request')); 
Promise.promisifyAll(request); 

//hardcoded url 
var url = 'http://shirts4mike.com/'; 

var urlSet = new Set(); 
var tshirtArray = []; 

var maxLevels = 3; // limit the recursion to this number of levels. 

function scrapePage(url_, levelCounter) { 
    // Bale out if : 
    // a) the target url_ has been visited already, 
    // b) maxLevels has been reached. 
    if(urlSet.has(url_) || levelCounter >= maxLevels) { 
     return Promise.resolve(); 
    } 
    urlSet.add(url_); 

    return request.getAsync(url_).then(function(response, html) { 
     var $; 
     if(response.statusCode !== 200) { 
      throw new Error('statusCode was not 200'); // will be caught below 
     } 
     $ = cheerio.load(html); 
     if($('[type=submit]').length > 0) { 
      // yay, it's a product page. 
      tshirtArray.push({ 
       price: $('.price').text(), 
       img: $('.shirt-picture').find("img").attr("src"), 
       title: $('body').find(".shirt-details > h1").text().slice(4), 
       url: url_, 
       date: moment().format('MMMM Do YYYY, h:mm:ss a') 
      }); 
     } 
     // find any shirt links on page represented by $, visit each link in turn, and scrape. 
     return Promise.all($("a[href*=shirt]").map(function(link) { 
      return scrapePage(link.href, levelCounter + 1); 
     }).get()); 
    }).catch(function(e) { 
     // ensure "success" even if scraping threw an error. 
     console.log(e); 
     return null; 
    }); 
} 

scrapePage(url, 0).then(convertJson2Csv); 

Как вы можете видеть, рекурсивное решение:

  • избегает повторения кода,
  • будет детализировать столько уровней, как вы хотите - определяется переменной maxLevels.

Примечание: Это все еще не очень хорошее решение. Здесь подразумевается неявное предположение, как и в исходном коде, что все страницы рубашек достижимы с домашней страницы сайта, только через «рубашечные» ссылки. Если бы рубашки были доступны, например, «одежда»> «рубашки», тогда код выше не найдет рубашек.