Saya menggunakan PhantomJS v1.4.1 untuk memuat beberapa halaman web. Saya tidak memiliki akses ke sisi server mereka, saya hanya mendapatkan tautan yang mengarah ke mereka. Saya menggunakan Phantom versi lama karena saya perlu mendukung Adobe Flash di halaman web itu.
Masalahnya adalah banyak situs web memuat asinkron konten minornya dan itulah mengapa callback onLoadFinished Phantom (analog untuk onLoad dalam HTML) diaktifkan terlalu dini ketika tidak semuanya masih dimuat. Adakah yang bisa menyarankan bagaimana saya bisa menunggu pemuatan penuh laman web, misalnya, tangkapan layar dengan semua konten dinamis seperti iklan?
javascript
events
phantomjs
nihil
sumber
sumber
Jawaban:
Pendekatan lain adalah dengan meminta PhantomJS menunggu sebentar setelah halaman dimuat sebelum melakukan render, seperti contoh rasterize.js biasa , tetapi dengan waktu tunggu yang lebih lama untuk memungkinkan JavaScript menyelesaikan pemuatan sumber daya tambahan:
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 } });
sumber
Saya lebih suka memeriksa
document.readyState
status secara berkala ( https://developer.mozilla.org/en-US/docs/Web/API/document.readyState ). Meskipun pendekatan ini agak kikuk, Anda dapat yakin bahwa di dalamonPageReady
fungsi Anda menggunakan dokumen yang dimuat sepenuhnya.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(); });
Penjelasan tambahan:
Menggunakan bersarang,
setTimeout
bukansetInterval
mencegahcheckReadyState
"tumpang tindih" dan kondisi balapan saat eksekusinya diperpanjang karena beberapa alasan acak.setTimeout
memiliki penundaan default 4ms ( https://stackoverflow.com/a/3580085/1011156 ) sehingga polling aktif tidak akan mempengaruhi kinerja program secara drastis.document.readyState === "complete"
berarti dokumen dimuat sepenuhnya dengan semua sumber daya ( https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness ).sumber
readyState
hanya akan terpicu setelah DOM dimuat penuh, namun<iframe>
elemen apa pun mungkin masih dimuat sehingga tidak benar-benar menjawab pertanyaan asliAnda dapat mencoba kombinasi dari contoh tunggu dan rasterisasi:
/** * 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); } }); }
sumber
Berikut adalah solusi yang menunggu semua permintaan sumber daya selesai. Setelah selesai, itu akan mencatat konten halaman ke konsol dan menghasilkan tangkapan layar dari halaman yang dirender.
Meskipun solusi ini dapat berfungsi sebagai titik awal yang baik, saya telah mengamati bahwa ini gagal sehingga jelas bukan solusi yang lengkap!
Saya tidak beruntung menggunakan
document.readyState
.Saya dipengaruhi oleh contoh waitfor.js yang ditemukan di halaman contoh 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); });
sumber
Mungkin Anda dapat menggunakan callback
onResourceRequested
danonResourceReceived
untuk mendeteksi pemuatan asinkron. Berikut adalah contoh penggunaan callback tersebut dari dokumentasinya :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);
Juga, Anda dapat melihat
examples/netsniff.js
contoh yang berfungsi.sumber
All the resource requests and responses can be sniffed using onResourceRequested and onResourceReceived
Dalam program saya, saya menggunakan beberapa logika untuk menilai apakah itu onload: menonton permintaan jaringannya, jika tidak ada permintaan baru pada 200ms terakhir, saya memperlakukannya sebagai onload.
Gunakan ini, setelah 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; } } }
sumber
Saya menemukan pendekatan ini berguna dalam beberapa kasus:
page.onConsoleMessage(function(msg) { // do something e.g. page.render });
Dibandingkan jika Anda memiliki laman, masukkan beberapa skrip di dalamnya:
<script> window.onload = function(){ console.log('page loaded'); } </script>
sumber
Saya menemukan solusi ini berguna dalam aplikasi NodeJS. Saya menggunakannya hanya dalam kasus putus asa karena meluncurkan batas waktu untuk menunggu pemuatan halaman penuh.
Argumen kedua adalah fungsi panggilan balik yang akan dipanggil setelah respons siap.
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);
sumber
Ini adalah implementasi dari jawaban Supr. Juga menggunakan setTimeout dan bukan setInterval seperti yang disarankan Mateusz Charytoniuk.
Phantomjs akan keluar dalam 1000ms jika tidak ada permintaan atau respon.
// 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();
sumber
Ini kode yang saya gunakan:
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); });
Pada dasarnya mengingat fakta bahwa Anda seharusnya tahu bahwa halaman telah didownload secara penuh ketika elemen tertentu muncul di DOM. Jadi skrip akan menunggu sampai ini terjadi.
sumber
Saya menggunakan campuran personnal dari
waitfor.js
contoh phantomjs .Ini
main.js
file saya :'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); } });
Dan
lib/waitFor.js
file tersebut (yang hanya merupakan copy dan pastewaifFor()
fungsi dariwaitfor.js
contoh phantomjs ):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 }
Metode ini tidak asinkron tetapi setidaknya saya yakin bahwa semua sumber daya telah dimuat sebelum saya mencoba menggunakannya.
sumber
Ini adalah pertanyaan lama, tetapi karena saya sedang mencari pemuatan halaman penuh tetapi untuk Spookyjs (yang menggunakan casperjs dan phantomjs) dan tidak menemukan solusi saya, saya membuat skrip sendiri untuk itu, dengan pendekatan yang sama seperti yang dianggap pengguna. Apa yang dilakukan pendekatan ini, untuk jangka waktu tertentu, jika halaman tidak menerima atau memulai permintaan apa pun, itu akan mengakhiri eksekusi.
Pada file casper.js (jika Anda menginstalnya secara global, jalurnya akan seperti /usr/local/lib/node_modules/casperjs/modules/casper.js) tambahkan baris berikut:
Di bagian atas file dengan semua vars global:
var waitResponseInterval = 500 var reqResInterval = null var reqResFinished = false var resetTimeout = function() {}
Kemudian di dalam fungsi "createPage (casper)" tepat setelah "var page = require ('webpage'). Create ();" tambahkan kode berikut:
resetTimeout = function() { if(reqResInterval) clearTimeout(reqResInterval) reqResInterval = setTimeout(function(){ reqResFinished = true page.onLoadFinished("success") },waitResponseInterval) } resetTimeout()
Kemudian di dalam "page.onResourceReceived = function onResourceReceived (resource) {" pada baris pertama tambahkan:
Lakukan hal yang sama untuk "page.onResourceRequested = function onResourceRequested (requestData, request) {"
Terakhir, pada "page.onLoadFinished = function onLoadFinished (status) {" pada baris pertama tambahkan:
if(!reqResFinished) { return } reqResFinished = false
Dan hanya itu, semoga yang ini membantu seseorang yang dalam kesulitan seperti saya. Solusi ini untuk casperjs tetapi bekerja langsung untuk Spooky.
Semoga berhasil !
sumber
ini adalah solusi saya yang berhasil untuk saya.
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); } });
sumber
Apakah gerakan Mouse saat halaman sedang dimuat seharusnya berfungsi.
page.sendEvent('click',200, 660); do { phantom.page.sendEvent('mousemove'); } while (page.loading);
MEMPERBARUI
Saat mengirimkan formulir, tidak ada yang dikembalikan, sehingga program dihentikan. Program tidak menunggu hingga halaman dimuat karena butuh beberapa detik untuk memulai pengalihan.
menyuruhnya untuk menggerakkan mouse sampai URL berubah ke halaman beranda memberi browser waktu sebanyak yang diperlukan untuk mengubahnya. kemudian menyuruhnya untuk menunggu halaman selesai memuat memungkinkan halaman untuk memuat penuh sebelum konten diambil.
page.evaluate(function () { document.getElementsByClassName('btn btn-primary btn-block')[0].click(); }); do { phantom.page.sendEvent('mousemove'); } while (page.evaluate(function() { return document.location != "https://www.bestwaywholesale.co.uk/"; })); do { phantom.page.sendEvent('mousemove'); } while (page.loading);
sumber