web-dev-qa-db-fra.com

Comment attendre le chargement de la page lors de l'utilisation de casperjs?

J'essaie de gratter une page Web qui a une forme avec beaucoup de listes déroulantes et les valeurs dans la forme sont interdépendantes. À plusieurs reprises, j'ai besoin du code pour attendre la fin de l'actualisation de la page. Par exemple, après avoir sélectionné une option dans la liste, le code doit attendre que la liste suivante soit remplie en fonction de cette sélection. Ce serait vraiment utile si quelqu'un pouvait donner des pointeurs, car étrangement, mon code ne fonctionne qu'après que j'ai donné autant d'instructions de journalisation inutiles, ce qui a créé un certain retard. Toute suggestion pour améliorer le code serait très utile.

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();
23
qwerty123

J'utilise la solution de contournement waitForSelector mentionnée par Arun ici: https://stackoverflow.com/a/22217657/1842033

C'est la meilleure solution que j'ai trouvée; L’inconvénient, c’est que vous devez savoir quel élément vous souhaitez charger. Je dis inconvénient, personnellement, je ne pense pas avoir rencontré une situation dans laquelle je n'ai pas eu quelque type de feedback disant que tout ce que j'attendais est arrivé

this.waitForSelector("{myElement}",
    function pass () {
        test.pass("Found {myElement}");
    },
    function fail () {
        test.fail("Did not load element {myElement}");
    },
    20000 // timeout limit in milliseconds
);

Bien que je suppose que vous pourriez utiliser waitForResource () ou quelque chose comme ça si vous n'aviez pas de retour visuel.

10
Algy Taylor

Ce que j'ai entrepris de faire pour contourner ce problème, lorsqu'il n'y a rien de spécifique à cibler et d'attendre dans la page rechargée, est d'utiliser les éléments suivants:

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 cette façon, je n'ai pas à compter sur un élément attendu spécifique dans la page suivante, j'ai essentiellement fait l'inverse. J'ai créé un sélecteur spécifique à surveiller, et l'exécution continue une fois que ce sélecteur ne correspond plus.

Pour ce qui est de mon intention, il suffisait de savoir que la page avait commencé à être rechargée. Je n'avais pas besoin d'attendre que la page suivante soit complètement rechargée. C'est ainsi que je pourrais alors déclencher certains appels waitForSelector sur des éléments pouvant exister avant et après le rechargement. Attendre que la classe temporaire ait été supprimée me permet de savoir que tout ce qui existait auparavant a été détruit depuis, alors ne craignez pas de sélectionner des éléments avant le rechargement.

7
Pebbl

Il semble qu'il n'y ait pas de solutions réelles . http://docs.casperjs.org/fr/latest/modules/casper.html#waitforselector est une solution de contournement disponible qui peut ne pas toujours fonctionner.

2
qwerty123

J'ai la même expérience en train de faire la même chose que toi. script de cette façon dans la perspective de l'utilisateur ne s'est jamais bien passé. il tombe en panne au milieu de nulle part et très peu fiable. Je faisais des recherches à partir de salesforce qui nécessitent également une connexion.

Vous devez garder le moins possible votre pas. script de manière cron. ne remplissez pas le formulaire/ne cliquez sur le bouton que si vous effectuez un test d'interface utilisateur. Je vous conseillerais de diviser le processus en deux parties

// 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)

mon script est maintenant pur phantomjs, le script casper continue à planter sans raison. Même Phantomjs n'est pas fiable. J'enregistre le résultat/le statut sur chaque recherche/téléchargement réussi, chaque fois qu'il y a une erreur, je quitte le script si le reste du résultat n'est pas prévisible (un bon résultat dans chrome est incorrect dans des fantômes).

1
wayne

J'ai trouvé cette question lorsque je cherchais une solution à un problème où l'action click () ou fill () rechargeait exactement les mêmes données dans un iframe enfant. Voici mon amélioration à Pebbl réponse:

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);
};

Cette solution suppose que la page utilise jQuery. Il ne devrait pas être difficile de le modifier pour les pages qui ne le font pas. unload_selector est un élément qui devrait être rechargé après un clic ou une soumission de formulaire. 

1
Lukasz

Puisque Casperjs est écrit pour les développeurs, on s’attend à savoir dans quel état la page chargée doit être et quels éléments doivent être disponibles pour définir un état chargé par une page.

Une option consiste à vérifier la présence, par exemple, d'une ressource javascript chargée à la fin de la page. 

Quel que soit le type de test utilisé, les résultats doivent être reproductibles à chaque fois; l’idempotence est donc essentielle. Pour que cela se produise, le testeur doit être capable de contrôler suffisamment l'environnement pour que cela se produise.

0
Lloyd Moore

Il suffit d’évaluer document.readyState pour être complete ou interactive. Puis c'est chargé.

Ceci est une implémentation avec un while, mais peut être fait avec intervalle ...

this.then(function () {
 while(this.evaluate(function () { return document.readyState != 'complete' && document.readyState != 'interactive'; })) {}
});
0
Puffy