phantomjs가 "전체"페이지로드를 기다리지 않습니다
PhantomJS v1.4.1을 사용하여 일부 웹 페이지를로드하고 있습니다. 서버 측에 액세스 할 수 없으며 링크를 가져옵니다. 해당 웹 페이지에서 Adobe Flash를 지원해야하므로 사용되지 않는 Phantom 버전을 사용하고 있습니다.
문제는 많은 웹 사이트가 사소한 콘텐츠를 비동기 적으로로드하는 것이므로 모든 것이 아직로드되지 않은 경우 Phantom의 onLoadFinished 콜백 (HTML의 onLoad에 대한 아날로그)이 너무 일찍 시작되었습니다. 누구나 웹 페이지가 가득 찰 때까지 기다릴 수있는 방법을 제안 할 수 있습니까 (예 : 광고와 같은 모든 동적 콘텐츠가 포함 된 스크린 샷)?
또 다른 방법은 일반적인 rasterize.js 예제에 따라 페이지를로드 한 후 PhantomJS에게 페이지로드 후 약간 기다렸다가 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
기본 지연이 4ms ( https://stackoverflow.com/a/3580085/1011156 )이므로 활성 폴링은 프로그램 성능에 큰 영향을 미치지 않습니다.
document.readyState === "complete"
문서에 모든 자원이 완전히로드되었음을 의미합니다 ( https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness ).
대기 및 래스터 화 예제의 조합을 시도해 볼 수 있습니다.
/**
* 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);
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
실제 예제를 볼 수 있습니다 .
내 프로그램에서는로드가 있는지 여부를 판단하기 위해 몇 가지 논리를 사용합니다. 네트워크 요청을보고, 지난 200ms 동안 새로운 요청이 없었 으면 온로드로 처리합니다.
onLoadFinish () 후에 이것을 사용하십시오.
function onLoadComplete(page, callback){
var waiting = []; // request id
var interval = 200; //ms time waiting new request
var timer = setTimeout( timeout, interval);
var max_retry = 3; //
var counter_retry = 0;
function timeout(){
if(waiting.length && counter_retry < max_retry){
timer = setTimeout( timeout, interval);
counter_retry++;
return;
}else{
try{
callback(null, page);
}catch(e){}
}
}
//for debug, log time cost
var tlogger = {};
bindEvent(page, 'request', function(req){
waiting.push(req.id);
});
bindEvent(page, 'receive', function (res) {
var cT = res.contentType;
if(!cT){
console.log('[contentType] ', cT, ' [url] ', res.url);
}
if(!cT) return remove(res.id);
if(cT.indexOf('application') * cT.indexOf('text') != 0) return remove(res.id);
if (res.stage === 'start') {
console.log('!!received start: ', res.id);
//console.log( JSON.stringify(res) );
tlogger[res.id] = new Date();
}else if (res.stage === 'end') {
console.log('!!received end: ', res.id, (new Date() - tlogger[res.id]) );
//console.log( JSON.stringify(res) );
remove(res.id);
clearTimeout(timer);
timer = setTimeout(timeout, interval);
}
});
bindEvent(page, 'error', function(err){
remove(err.id);
if(waiting.length === 0){
counter_retry = 0;
}
});
function remove(id){
var i = waiting.indexOf( id );
if(i < 0){
return;
}else{
waiting.splice(i,1);
}
}
function bindEvent(page, evt, cb){
switch(evt){
case 'request':
page.onResourceRequested = cb;
break;
case 'receive':
page.onResourceReceived = cb;
break;
case 'error':
page.onResourceError = cb;
break;
case 'timeout':
page.onResourceTimeout = cb;
break;
}
}
}
이 접근법이 어떤 경우에는 유용하다는 것을 알았습니다.
page.onConsoleMessage(function(msg) {
// do something e.g. page.render
});
페이지를 소유 한 경우보다 일부 스크립트를 내부에 넣습니다.
<script>
window.onload = function(){
console.log('page loaded');
}
</script>
이 솔루션이 NodeJS 앱에서 유용하다는 것을 알았습니다. 전체 페이지로드를 기다릴 때 시간 초과가 발생하기 때문에 필사적 인 경우에만 사용합니다.
두 번째 인수는 응답이 준비되면 호출되는 콜백 함수입니다.
phantom = require('phantom');
var fullLoad = function(anUrl, callbackDone) {
phantom.create(function (ph) {
ph.createPage(function (page) {
page.open(anUrl, function (status) {
if (status !== 'success') {
console.error("pahtom: error opening " + anUrl, status);
ph.exit();
} else {
// timeOut
global.setTimeout(function () {
page.evaluate(function () {
return document.documentElement.innerHTML;
}, function (result) {
ph.exit(); // EXTREMLY IMPORTANT
callbackDone(result); // callback
});
}, 5000);
}
});
});
});
}
var callback = function(htmlBody) {
// do smth with the htmlBody
}
fullLoad('your/url/', callback);
이것은 Supr의 답변을 구현 한 것입니다. 또한 Mateusz Charytoniuk이 제안한대로 setInterval 대신 setTimeout을 사용합니다.
Phantomjs는 요청이나 응답이 없으면 1000ms 후에 종료됩니다.
// load the module
var webpage = require('webpage');
// get timestamp
function getTimestamp(){
// or use Date.now()
return new Date().getTime();
}
var lastTimestamp = getTimestamp();
var page = webpage.create();
page.onResourceRequested = function(request) {
// update the timestamp when there is a request
lastTimestamp = getTimestamp();
};
page.onResourceReceived = function(response) {
// update the timestamp when there is a response
lastTimestamp = getTimestamp();
};
page.open(html, function(status) {
if (status !== 'success') {
// exit if it fails to load the page
phantom.exit(1);
}
else{
// do something here
}
});
function checkReadyState() {
setTimeout(function () {
var curentTimestamp = getTimestamp();
if(curentTimestamp-lastTimestamp>1000){
// exit if there isn't request or response in 1000ms
phantom.exit();
}
else{
checkReadyState();
}
}, 100);
}
checkReadyState();
이것은 내가 사용하는 코드입니다.
var system = require('system');
var page = require('webpage').create();
page.open('http://....', function(){
console.log(page.content);
var k = 0;
var loop = setInterval(function(){
var qrcode = page.evaluate(function(s) {
return document.querySelector(s).src;
}, '.qrcode img');
k++;
if (qrcode){
console.log('dataURI:', qrcode);
clearInterval(loop);
phantom.exit();
}
if (k === 50) phantom.exit(); // 10 sec timeout
}, 200);
});
기본적으로 주어진 요소가 DOM에 나타날 때 페이지가 완전히 다운로드되었음을 알고 있어야합니다. 따라서 스크립트는 이러한 상황이 발생할 때까지 기다립니다.
이것은 오래된 질문이지만 전체 페이지로드를 찾고 있었지만 Spookyjs (casperjs 및 phantomjs를 사용하는)를 찾고 솔루션을 찾지 못했기 때문에 사용자 deemstone과 동일한 접근 방식으로 자체 스크립트를 만들었습니다. 이 접근 방식은 주어진 시간 동안 페이지가 요청을 수신하거나 시작하지 않으면 실행을 종료합니다.
casper.js 파일에서 (전역으로 설치 한 경우 경로는 /usr/local/lib/node_modules/casperjs/modules/casper.js와 같습니다) 다음 행을 추가하십시오.
모든 전역 변수가있는 파일 맨 위에 :
var waitResponseInterval = 500
var reqResInterval = null
var reqResFinished = false
var resetTimeout = function() {}
그런 다음 "var page = require ( 'webpage'). create ();"바로 뒤에있는 "createPage (casper)"함수 내부 다음 코드를 추가하십시오.
resetTimeout = function() {
if(reqResInterval)
clearTimeout(reqResInterval)
reqResInterval = setTimeout(function(){
reqResFinished = true
page.onLoadFinished("success")
},waitResponseInterval)
}
resetTimeout()
그런 다음 첫 번째 행의 "page.onResourceReceived = function onResourceReceived (resource) {"안에 다음을 추가하십시오.
resetTimeout()
"page.onResourceRequested = function onResourceRequested (requestData, request) {"에 대해서도 동일하게 수행하십시오.
마지막으로 첫 번째 줄의 "page.onLoadFinished = function onLoadFinished (status) {"에 다음을 추가하십시오.
if(!reqResFinished)
{
return
}
reqResFinished = false
그게 다야, 나처럼 곤경에 처한 사람을 돕기를 바랍니다. 이 솔루션은 casperjs 용이지만 Spooky 용으로 직접 작동합니다.
행운을 빕니다 !
나는 phantomjs waitfor.js
예제 의 개인적 혼합을 사용합니다 .
이것은 내 main.js
파일입니다.
'use strict';
var wasSuccessful = phantom.injectJs('./lib/waitFor.js');
var page = require('webpage').create();
page.open('http://foo.com', function(status) {
if (status === 'success') {
page.includeJs('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js', function() {
waitFor(function() {
return page.evaluate(function() {
if ('complete' === document.readyState) {
return true;
}
return false;
});
}, function() {
var fooText = page.evaluate(function() {
return $('#foo').text();
});
phantom.exit();
});
});
} else {
console.log('error');
phantom.exit(1);
}
});
그리고 lib/waitFor.js
파일 ( waifFor()
phantomjs waitfor.js
예제 의 함수를 복사하여 붙여 넣습니다 ) :
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
start = new Date().getTime(),
condition = false,
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 condi>
clearInterval(interval); //< Stop this interval
}
}
}, 250); //< repeat check every 250ms
}
이 방법은 비동기식이 아니지만 적어도 모든 리소스가 사용되기 전에로드되었다고 확신합니다.
이것은 내 솔루션으로 나를 위해 일했습니다.
page.onConsoleMessage = function(msg, lineNum, sourceId) {
if(msg=='hey lets take screenshot')
{
window.setInterval(function(){
try
{
var sta= page.evaluateJavaScript("function(){ return jQuery.active;}");
if(sta == 0)
{
window.setTimeout(function(){
page.render('test.png');
clearInterval();
phantom.exit();
},1000);
}
}
catch(error)
{
console.log(error);
phantom.exit(1);
}
},1000);
}
};
page.open(address, function (status) {
if (status !== "success") {
console.log('Unable to load url');
phantom.exit();
} else {
page.setContent(page.content.replace('</body>','<script>window.onload = function(){console.log(\'hey lets take screenshot\');}</script></body>'), address);
}
});
참고 URL : https://stackoverflow.com/questions/11340038/phantomjs-not-waiting-for-full-page-load
'Programing' 카테고리의 다른 글
라이브 록의 좋은 예? (0) | 2020.06.24 |
---|---|
계속하기 전에 하나의 기능이 완료되기를 기다리는 적절한 방법은 무엇입니까? (0) | 2020.06.24 |
프로그래밍 방식으로 APK 설치 / 제거 (PackageManager 및 의도) (0) | 2020.06.24 |
Java POJO에서 필드, 변수, 속성 및 속성의 차이점은 무엇입니까? (0) | 2020.06.24 |
PowerShell에서 변수에 저장된 명령 실행 (0) | 2020.06.24 |