Рамки обещаний для PhantomJS?
Я новичок в PhantomJS. Я хочу загрузить страницу, очистить ее ссылки, а затем открыть каждый из них последовательно, по одному за раз, возможно даже с задержкой между каждым запросом. У меня возникли проблемы с тем, чтобы один из них запускался за другим, поэтому я подумал, что, возможно, я смогу использовать promises для решения этой проблемы, но я не думаю, что библиотеки Node работают с Phantom. Каждый пример, который я видел до сих пор, открывает одну страницу, а затем завершает работу.
Вот что у меня есть:
var page = require('webpage').create();
page.open('http://example.com/secretpage', function(status) {
console.log(status);
if(status !== 'success') {
console.log('Unable to access network');
} else {
var links = page.evaluate(function() {
var nodes = [];
var matches = document.querySelectorAll('.profile > a');
for(var i = 0; i < matches.length; ++i) {
nodes.push(matches[i].href);
}
return nodes;
});
links.forEach(function(link) {
console.log(link);
page.open(link, function(status) { // <---- tries opening every page at once
console.log(status);
var name = page.evaluate(function() {
return document.getElementById('username').innerHTML;
});
console.log(name);
page.render('profiles/'+name + '.png');
});
});
}
// phantom.exit();
});
Есть ли способ, по которому я могу последовательно открывать каждую ссылку?
Ответы
Ответ 1
Для этого типичного сценария я использую async.js и особенно очередь компонент.
Вот очень простая реализация
phantom.injectJs('async.js');
var q = async.queue(function (task, callback) {
page.open(task.url, function(status) { // <---- tries opening every page at once
if(status !== 'success') {
console.log('Unable to open url > '+task.url);
} else {
console.log('opened '+task.url);
//do whatever you want here ...
page.render(Date.now() + '.png');
}
callback();
});
}, 1);
// assign a callback
q.drain = function() {
console.log('all urls have been processed');
phantom.exit();
}
var page = require('webpage').create();
page.open('http://phantomjs.org/', function(status) {
console.log(status);
if(status !== 'success') {
console.log('Unable to access network');
} else {
var links = page.evaluate(function() {
var nodes = [];
var matches = document.querySelectorAll('a');
for(var i = 0; i < matches.length; ++i) {
nodes.push(matches[i].href);
}
return nodes;
});
links.forEach(function(link) {
q.push({url: link}, function (err) {
console.log('finished processing '+link);
});
});
}
});
URL-адреса добавляются в очередь и будут обрабатываться параллельно (до предела concurrency, один здесь). Я повторно использую один экземпляр страницы, но это необязательно.
Как я уже делал этот гусениц в прошлом, позвольте мне дать вам еще два совета:
- Не загружайте изображения, чтобы ускорить тестирование.
- href иногда относителен, поэтому сначала проверьте, действительно ли он действительный url
Ответ 2
[EDIT]
Вам нужно поставить в очередь. Я изменил ваш код и добавил в него простой механизм очереди.
var page = require('webpage').create();
page.open('http://example.com/secretpage', function(status) {
console.log(status);
if (status !== 'success') {
console.log('Unable to access network');
} else {
var links = page.evaluate(function() {
var nodes = [];
var matches = document.querySelectorAll('.profile > a');
for (var i = 0; i < matches.length; ++i) {
nodes.push(matches[i].href);
}
return nodes;
});
var pointer = 0,
linksCount = links.length,
q = function() {
var link = links[pointer];
console.log(link);
page.open(link, function(status) { // <---- tries opening every page at once
console.log(status);
var name = page.evaluate(function() {
return document.getElementById('username').innerHTML;
});
console.log(name);
page.render('profiles/' + name + '.png');
// pointer increaments;
pointer++;
if (pointer == linksCount) {
// recursion exit
phantom.exit();
}
else {
// recursive cal;
q();
}
});
};
// start queue to load links one by one
q();
});
ПРИМЕЧАНИЕ. foreach не дожидается загрузки каждой страницы, а загрузка на страницу является асинхронной. Отсюда и ваша проблема.
Вы можете прочитать ответ на аналогичный вопрос о CasperJS (обертке вокруг PhantomJS) с кодом, как справиться с этим из Как сделать цикл в casperjs
Ответ 3
Вы можете использовать Phantom-promise A PhantomJS bridge with a promise based api.
или phantom PhantomJS integration module for NodeJS
.
Другая возможность для открытия каждой ссылки в последовательности
В принципе у вас есть 3 варианта, но вы можете взять alook Casperjs Navigation scripting & testing for PhantomJS and SlimerJS