Bluebird обещает и async.forEach итерации
Я впервые задаю вопрос, поэтому, пожалуйста, потерпите меня. Я пытаюсь написать скребок содержимого в node.js. Моя программа перейдет на целевую страницу сайта, получит ссылку на следующую страницу и ссылку для следующего пакета страниц. Моя проблема в том, что когда мне приходится перебирать массив ссылок, переходить на эту страницу и получать там информацию об отходах. Я пытаюсь выполнить итерацию async.forEach, но она заканчивается после последнего then() в моих цепочечных обещаниях. Многие из представленных здесь концепций являются новыми для меня, поэтому я уверен, что этот код не совсем понятен. Любая помощь будет оценена.
var sX = require('scrapper-x');
var request = require('request');
var async = require('async');
var Promise = require('bluebird');
var request = (require('request'));
var colors = require('colors');
var baseURL = 'http://shirts4mike.com';
var arrShirts = [
{
link: 'shirt.php?id=101',
img: 'some img'
},
{
link: 'shirt.php?id=102',
img: 'some img'
},
{
link: 'shirt.php?id=103',
img: 'some img'
}
];
var page = '';
var configMain = {
repeatItemGroup: '.nav > li',
dataFormat: {
link: {
selector: 'li > a',
type: 'attr:href'
}
}
};
var configShirts = {
repeatItemGroup: '.products > li',
dataFormat: {
link: {
selector: 'li > a',
type: 'attr:href'
},
img: {
selector: 'li > a > img',
type: 'attr:src'
}
}
};
var configDetails = {
repeatItemGroup: '.shirt-details',
dataFormat: {
price: {
selector: '.price',
type: 'text'
},
title: {
selector: '.shirt-details > h1',
type: 'text'
}
}
};
function getPage(url, config) {
return new Promise(function(resolve, reject) {
request(url, function(error, response, body, shirt) {
if (error) {
console.log('error');
reject();
}
if (!error && response.statusCode == 200) {
detail = sX.scrape(body, config);
resolve(detail);
}
});
});
}
function getDetailPage(arr, config) {
return new Promise(function(resolve, reject) {
async.forEach( arr, function( item, callback) {
request('http://www.shirts4mike.com' + '/' + item.link, function(error, response, body, item) {
if (!error && response.statusCode == 200) {
detail = sX.scrape(body, configDetails);
console.log('Item: ', detail);
}
});
callback();
});
resolve(detail);
});
}
getPage(baseURL, configMain).then( function(data) {
for (var i = 0; i < data.length; i++) {
//console.log('getMainPage: ', scrappedResult[i].link);
if (data[i].link.search('shirt') !== -1) {
page = '/' + data[i].link;
console.log(page.yellow);
baseURL += page;
return baseURL;
}
}
}).then( function(baseURL) {
return getPage(baseURL, configShirts);
}).then( function(data) {
for (var i = 0; i < data.length; i++) {
//console.log(data[i].link);
if (data[i].link.search('shirt') !== -1) {
arrShirts.push(data[i]);
console.log("arrShirts[" + i + "]: " + arrShirts[i].link);
}
}
return arrShirts;
}).then( function(arr) {
return getDetailPage(arrShirts, configDetails);
}).then(function (data) {
console.log("end: ", data);
}).catch( function(err) {
console.log(err);
});
1 ответ
Я бы предложил кучу изменений.
Прекратите использовать глобальные переменные или переменные с более высокой областью действия или необъявленные переменные. Объявите локальные переменные там, где они используются / нужны, и не делите переменные с другими функциями.
Не смешивайте асинхронную библиотеку с обещаниями. Поскольку вы уже обещаете свои асинхронные функции, используйте функции обещания для управления несколькими асинхронными операциями. Вы можете просто полностью удалить асинхронную библиотеку из этого проекта.
Обещание на самом низком уровне, которое является практичным, поэтому все пользователи более высокого уровня могут просто использовать обещания напрямую. Таким образом, вместо того, чтобы обещать
getPage()
надо обещатьrequest()
а затем использовать это вgetPage()
,Поскольку у вас есть библиотека обещаний Bluebird, вы можете использовать ее итерационные функции более высокого уровня, такие как
Promise.map()
итерировать вашу коллекцию, собрать все результаты и вернуть одно обещание, которое сообщит вам, когда все будет сделано.
Используя эти концепции, вы можете использовать такой код:
// promisified request()
function getRequest(url) {
return new Promise(function(resolve, reject) {
request(url, function(err, response, body) {
if (err) {
reject(err);
} else {
resolve(body);
}
});
});
}
// resolves to scraped contents for one page
function getPage(url, config) {
return getRequest(url).then(function(body) {
return sX.scrape(body, config);
});
}
// resolves to an array of scraped contents for an array of objects
// that each have a link in them
function getDetailPage(arr, config) {
return Promise.map(arr, function(item) {
return getPage('http://www.shirts4mike.com' + '/' + item.link);
});
}