javascript - ¿Cómo esperar a que se cargue la página cuando se usa casperjs?
phantomjs (7)
Dado que Casperjs está escrito para desarrolladores, se espera que se sepa en qué estado debería estar la página cargada y qué elementos deberían estar disponibles para definir un estado cargado en la página.
Una opción es verificar la presencia de, por ejemplo, un recurso javascript que se carga al final de la página.
Cuando se ejecuta cualquier tipo de prueba, los resultados deben ser reproducibles cada vez y, por lo tanto, la idempotencia es esencial. Para que esto suceda, el probador debe poder controlar el entorno lo suficiente para que esto suceda.
Estoy intentando raspar una página web que tiene un formulario con muchos menús desplegables y los valores en el formulario son interdependientes. En muchos puntos necesito el código para esperar hasta que se complete la actualización de la página. Por ejemplo, después de seleccionar una opción de la lista, el código debe esperar hasta que la siguiente lista se complete en función de esta selección. Sería de gran ayuda si alguien pudiera dar punteros porque, de forma extraña, mi código funciona solo después de haber dado tantas declaraciones de registro innecesarias que, a su vez, generaron cierto retraso. Cualquier sugerencia para mejorar el código sería muy útil.
var casper = require(''casper'').create({
verbose: true,
logLevel: ''debug'',
userAgent: ''Mozilla/5.0 poi poi poi (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22'',
pageSettings: {}
});
casper.start(''http://www.abc.com'', function () {
console.log("casper started");
this.fill(''form[action="http://www.abc.com/forum/member.php"]'', {
quick_username: "qwe",
quick_password: "qwe"
}, true);
this.capture(''screen.png'');
});
casper.thenOpen("http://www.abc.com/search/index.php").then(function () {
this.click(''input[type="checkbox"][name="firstparam"]'');
this.click(''a#poi'');
casper.evaluate(function () {
document.getElementsByName("status")[0].value = 1;
document.getElementsByName("state")[0].value = 1078;
changeState(); //This function is associated with the dropdown ie state
and the page reloads at this point. Only after complete refresh the code shoud execute! How can this be achieved?
return true;
});
this.echo(''Inside the first thenOpen'' + this.evaluate(function () {
return document.search.action;
}));
});
casper.then(function () {
this.capture("poi.png");
console.log(''just before injecting jquery'');
casper.page.injectJs(''./jquery.js'');
this.click(''input[type="checkbox"][name="or"]'');
this.evaluate(function () {
$(''.boxline .filelist input:checkbox[value=18127]'').attr("checked", true);
});
this.echo(''Just before pressing the add college button'' + this.evaluate(function () {
return document.search.action;
}));
this.capture(''collegeticked.png'');
if (this.exists(''input[type="button"][name="niv"]'')) {
this.echo(''button is there'');
} else {
this.echo(''button is not there'');
}
this.echo("Going to print return value");
this.click(''input[type="button"][name="poi"]''); // This click again causes a page refresh. Code should wait at this point for completion.
this.echo(''Immediately after pressing the add college btn getPresentState()'' + this.evaluate(function () {
return getPresentState();
}));
this.echo(''Immediately after pressing add colleg button'' + this.evaluate(function () {
return document.search.action;
}));
this.capture(''iu.png'');
});
casper.then(function () {
console.log(''just before form submit'');
this.click(''form[name="search"] input[type="submit"]''); //Again page refresh. Wait.
this.echo(''Immediately after search btn getPresentState()'' + this.evaluate(function () {
return getPresentState();
}));
this.echo(''Immediately after search button-action'' + this.evaluate(function () {
return document.search.action;
}));
this.capture("mnf.png");
});
casper.then(function () {
casper.page.injectJs(''./jquery.js'');
this.capture("resultspage.png");
this.echo(''Page title is: '' + this.evaluate(function () {
return document.title;
}), ''INFO'');
var a = casper.evaluate(function () {
return $(''tbody tr td.tdbottom:contains("tye") '').siblings().filter($(''td>a'').parent());
});
console.log("ARBABU before" + a.length);
});
casper.run();
Encontré esta pregunta al buscar una solución a un problema donde la acción click () o fill () recarga exactamente los mismos datos en un iframe secundario. Aquí está mi mejora a la respuesta de Pebbl :
casper.clickAndUnload = function (click_selector, unload_selector, callback, timeout) {
var classname = ''reload-'' + (new Date().getTime());
this.evaluate(function (unload_selector, classname) {
$(unload_selector).addClass(classname);
}, unload_selector, classname);
this.thenClick(click_selector);
this.waitWhileSelector(unload_selector + ''.'' + classname, callback, timeout);
};
casper.fillAndUnload = function (form_selector, data, unload_selector, callback, timeout) {
var classname = ''reload-'' + (new Date().getTime());
this.evaluate(function (unload_selector, classname) {
$(unload_selector).addClass(classname);
}, unload_selector, classname);
this.fill(form_selector, data, true);
this.waitWhileSelector(unload_selector + ''.'' + classname, callback, timeout);
};
Esta solución supone que la página usa jQuery. No debería ser difícil modificarlo para las páginas que no lo hacen. unload_selector
es un elemento que se espera que se vuelva a cargar después de hacer clic o enviar el formulario.
He estado usando la ''solución'' de waitForSelector mencionada por Arun aquí: https://.com/a/22217657/1842033
Es la mejor solución que he encontrado; El ''inconveniente'' es que debes tener en cuenta qué elemento esperas cargar. Digo un inconveniente, personalmente no creo que haya encontrado una situación en la que no haya tenido algún tipo de comentarios que digan que todo lo que estoy esperando ha sucedido.
this.waitForSelector("{myElement}",
function pass () {
test.pass("Found {myElement}");
},
function fail () {
test.fail("Did not load element {myElement}");
},
20000 // timeout limit in milliseconds
);
Aunque supongo que podrías usar waitForResource() o algo así si no tuvieras retroalimentación visual.
Lo que he emprendido para solucionar este problema, cuando no hay nada específico para apuntar y esperar en la página recargada, es usar lo siguiente:
var classname = ''reload-'' + (new Date().getTime()),
callback = function(){},
timeout = function(){};
/// It happens when they change something...
casper.evaluate(function(classname){
document.body.className += '' '' + classname;
}, classname);
casper.thenClick(''#submit''); /// <-- will trigger a reload of the page
casper.waitWhileSelector(''body.'' + classname, callback, timeout);
De esta manera no tengo que confiar en un elemento específico esperado en la página siguiente, básicamente he hecho lo inverso. He creado un selector específico para vigilar, y la ejecución avanza una vez que el selector no coincide.
Para mis intenciones y propósitos, era suficiente saber que la página había comenzado a recargarse, no tenía que esperar hasta que la siguiente página se hubiera recargado completamente. Esto es para que pueda desencadenar ciertas llamadas a waitForSelector
en elementos que pueden haber existido antes y después de la recarga. Esperar hasta que se haya eliminado la clase temporal me permite saber que todo lo que existía antes ha sido destruido, por lo que no hay miedo de seleccionar elementos antes de la recarga.
Parece que no hay soluciones reales. http://docs.casperjs.org/en/latest/modules/casper.html#waitforselector es una solución disponible que puede no funcionar siempre.
Solo evalúe document.readyState
para que sea complete
o interactive
. Entonces se carga.
Esta es una implementación con un while
, pero tal vez se puede hacer con un intervalo ...
this.then(function () {
while(this.evaluate(function () { return document.readyState != ''complete'' && document.readyState != ''interactive''; })) {}
});
Tengo la misma experiencia haciendo lo mismo que tú. Guión de esta manera en la perspectiva del usuario nunca ha ido bien Se estrella en medio de la nada y es muy poco fiable. Estaba haciendo búsquedas de salesforce que también requieren inicio de sesión.
Necesitas mantener tus pasos lo mínimo posible. Script en forma cron. no haga el relleno del formulario / clic del botón a menos que esté haciendo pruebas de interfaz de usuario. Te aconsejaría que dividieras el proceso en dos partes.
// this part do search and find out the exact url of your screen capture.
// save it in a db/csv file
1 - start by POST to http://www.abc.com/forum/member.php with username password in body.
2 - POST/GET to http://www.abc.com/search/index.php with your search criteria, you look at what the website require. if they do POST, then POST.
// second part read your input
1 - login same as first part.
2 - casper forEach your input save your capture. (save the capture result in db/csv)
mi script ahora es phantomjs puro, el script de casper simplemente sigue fallando sin ninguna razón. Incluso phantomjs no es confiable. Guardo el resultado / estado en cada búsqueda / descarga exitosa, siempre que haya un error, salgo del script si el resto del resultado es impredecible (el resultado bueno en Chrome resulta malo en Phantomjs).