我正在使用PhantomJS v1.4.1 来加载一些网页。我无权访问他们的服务器端,我只是获得指向他们的链接。我正在使用 Phantom 的过时版本,因为我需要在该网页上支持 Adobe Flash。
问题是许多网站都在异步加载它们的次要内容,这就是为什么 Phantom 的 onLoadFinished 回调(类似于 HTML 中的 onLoad)在并非所有内容都已加载时过早触发的原因。任何人都可以建议我如何等待网页满载来制作,例如,包含所有动态内容(如广告)的屏幕截图?
我正在使用PhantomJS v1.4.1 来加载一些网页。我无权访问他们的服务器端,我只是获得指向他们的链接。我正在使用 Phantom 的过时版本,因为我需要在该网页上支持 Adobe Flash。
问题是许多网站都在异步加载它们的次要内容,这就是为什么 Phantom 的 onLoadFinished 回调(类似于 HTML 中的 onLoad)在并非所有内容都已加载时过早触发的原因。任何人都可以建议我如何等待网页满载来制作,例如,包含所有动态内容(如广告)的屏幕截图?
另一种方法是让 PhantomJS 在页面加载后等待一段时间,然后再进行渲染,就像常规的rasterize.js示例一样,但超时时间更长,以允许 JavaScript 完成加载其他资源:
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit();
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 1000); // Change timeout as required to allow sufficient time
}
});
我宁愿定期检查document.readyState
状态(https://developer.mozilla.org/en-US/docs/Web/API/document.readyState)。虽然这种方法有点笨拙,但您可以确定onPageReady
您正在使用完整加载的文件内部函数。
var page = require("webpage").create(),
url = "http://example.com/index.html";
function onPageReady() {
var htmlContent = page.evaluate(function () {
return document.documentElement.outerHTML;
});
console.log(htmlContent);
phantom.exit();
}
page.open(url, function (status) {
function checkReadyState() {
setTimeout(function () {
var readyState = page.evaluate(function () {
return document.readyState;
});
if ("complete" === readyState) {
onPageReady();
} else {
checkReadyState();
}
});
}
checkReadyState();
});
补充说明:
当由于某些随机原因延长执行时间时,使用嵌套setTimeout
而不是setInterval
防止checkReadyState
“重叠”和竞争条件。setTimeout
默认延迟为 4 毫秒(https://stackoverflow.com/a/3580085/1011156),因此主动轮询不会显着影响程序性能。
document.readyState === "complete"
意味着该文档已完全加载了所有资源(https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness)。
您可以尝试结合使用 waitfor 和 rasterize 示例:
/**
* See https://github.com/ariya/phantomjs/blob/master/examples/waitfor.js
*
* Wait until the test condition is true or a timeout occurs. Useful for waiting
* on a server response or for a ui change (fadeIn, etc.) to occur.
*
* @param testFx javascript condition that evaluates to a boolean,
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
* as a callback function.
* @param onReady what to do when testFx condition is fulfilled,
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
* as a callback function.
* @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
*/
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
start = new Date().getTime(),
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()), //< defensive code
interval = setInterval(function() {
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if(!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
console.log("'waitFor()' timeout");
phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
clearInterval(interval); //< Stop this interval
}
}
}, 250); //< repeat check every 250ms
};
var page = require('webpage').create(), system = require('system'), address, output, size;
if (system.args.length < 3 || system.args.length > 5) {
console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]');
console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
phantom.exit(1);
} else {
address = system.args[1];
output = system.args[2];
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
size = system.args[3].split('*');
page.paperSize = size.length === 2 ? {
width : size[0],
height : size[1],
margin : '0px'
} : {
format : system.args[3],
orientation : 'portrait',
margin : {
left : "5mm",
top : "8mm",
right : "5mm",
bottom : "9mm"
}
};
}
if (system.args.length > 4) {
page.zoomFactor = system.args[4];
}
var resources = [];
page.onResourceRequested = function(request) {
resources[request.id] = request.stage;
};
page.onResourceReceived = function(response) {
resources[response.id] = response.stage;
};
page.open(address, function(status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit();
} else {
waitFor(function() {
// Check in the page if a specific element is now visible
for ( var i = 1; i < resources.length; ++i) {
if (resources[i] != 'end') {
return false;
}
}
return true;
}, function() {
page.render(output);
phantom.exit();
}, 10000);
}
});
}
这是一个等待所有资源请求完成的解决方案。完成后,它会将页面内容记录到控制台并生成渲染页面的屏幕截图。
虽然这个解决方案可以作为一个很好的起点,但我观察到它失败了,所以它绝对不是一个完整的解决方案!
我没有太多运气使用document.readyState
.
我被影响waitfor.js信中例如phantomjs例子页面。
var system = require('system');
var webPage = require('webpage');
var page = webPage.create();
var url = system.args[1];
page.viewportSize = {
width: 1280,
height: 720
};
var requestsArray = [];
page.onResourceRequested = function(requestData, networkRequest) {
requestsArray.push(requestData.id);
};
page.onResourceReceived = function(response) {
var index = requestsArray.indexOf(response.id);
if (index > -1 && response.stage === 'end') {
requestsArray.splice(index, 1);
}
};
page.open(url, function(status) {
var interval = setInterval(function () {
if (requestsArray.length === 0) {
clearInterval(interval);
var content = page.content;
console.log(content);
page.render('yourLoadedPage.png');
phantom.exit();
}
}, 500);
});
也许您可以使用onResourceRequested
和onResourceReceived
回调来检测异步加载。这是从他们的文档中使用这些回调的示例:
var page = require('webpage').create();
page.onResourceRequested = function (request) {
console.log('Request ' + JSON.stringify(request, undefined, 4));
};
page.onResourceReceived = function (response) {
console.log('Receive ' + JSON.stringify(response, undefined, 4));
};
page.open(url);
此外,您可以查看examples/netsniff.js
一个工作示例。