Как дождаться загрузки страницы при использовании casperjs?

Я пытаюсь очистить веб-страницу, которая имеет форму со многими выпадающими списками, а значения в форме взаимозависимы. Во многих случаях мне нужен код, чтобы дождаться обновления страницы. Например, после выбора опции из списка код должен подождать, пока следующий список не будет заполнен на основе этого выбора. Было бы очень полезно, если бы кто-то мог дать указатели, потому что, как ни странно, мой код работает только после того, как я дал так много ненужных операторов регистрации, которые в свою очередь создали некоторая задержка. Любые предложения по улучшению кода были бы очень полезны.

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

7 ответов


Я использую обходной путь waitForSelector, упомянутый Arun здесь: https://stackoverflow.com/a/22217657/1842033

Это лучшее решение, которое я нашел; "недостаток" как бы в том, что вам нужно знать, какой элемент вы ожидаете загрузить. Я говорю недостаток, лично я не думаю, что я столкнулся с ситуацией, когда у меня не было некоторые вид обратной связи сказать, что я ждали случилось

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

хотя я думаю, вы могли бы использовать waitForResource() или что-то в этом роде, если у вас не было визуальной обратной связи.


что я предпринял, чтобы обойти эту проблему, когда нет ничего конкретного для target и ждать на странице перезагрузки, это использовать следующее:

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

таким образом, мне не нужно полагаться на конкретный ожидаемый элемент на следующей странице, Я в основном сделал обратное. Я создал определенный селектор, чтобы следить за ним, и выполнение продолжается, как только этот селектор не соответствует.

для моих намерений и целей было достаточно знать, что страница имела начав перезагрузку, мне не нужно было ждать, пока следующая страница полностью перезагрузится. Это для того, чтобы я мог тогда вызвать определенные waitForSelector вызывает элементы, которые могли существовать как до, так и после перезагрузки. Ожидание, пока временный класс будет удален, дает мне знать, что все, что существовало раньше, с тех пор было уничтожено, поэтому не бойтесь выбирать элементы до перезагрузки.


кажется, что нет реальных решений. http://docs.casperjs.org/en/latest/modules/casper.html#waitforselector является доступным обходным путем, который может работать не всегда.


У меня тот же опыт, что и у вас. сценарий таким образом в пользовательской перспективе никогда не шел хорошо. он разбился в середине нигде и очень ненадежен. Я выполнял поиск из salesforce, который также требует входа в систему.

вы должны держать свой шаг как можно меньше. сценарий в cron-задании. не заполняйте форму / нажмите кнопку, если вы не выполняете тестирование пользовательского интерфейса. Я бы посоветовал вам разбить процесс на две части

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

мой скрипт теперь чиста phantomjs, сценарий Каспера просто продолжает сбой без причины. даже phantomjs ненадежен. Я сохраняю результат / статус при каждом успешном поиске / загрузке, всякий раз, когда есть ошибка, Я выхожу из скрипта, если не остальная часть результата непредсказуема(хороший результат в chrome оказывается плохим в phantomjs).


Я нашел этот вопрос при поиске решения проблемы, когда действие click() или fill() перезагружает точно такие же данные в дочернем iframe. Вот мое улучшение 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);
};

это решение предполагает, что страница использует jQuery. Это не должно быть трудно изменить его для страниц, которые не. unload_selector - элемент, который, как ожидается, будет перезагружен после щелчка или отправки формы.


поскольку Casperjs написан для разработчиков, ожидается, что известно, в каком состоянии должна быть загруженная страница и какие элементы должны быть доступны для определения состояния, загруженного страницей.

один из вариантов-проверить наличие, например, ресурса javascript, который загружается в конце страницы.

при выполнении любого типа теста результаты должны воспроизводиться каждый раз, и поэтому идемпотентность имеет важное значение. Для этого тестер должен уметь достаточно контролировать окружающую среду, чтобы это произошло.


просто оценке document.readyState на complete или interactive. Тогда он заряжен.

это реализация с while, но, возможно, можно сделать с интервалом...

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