フォームに多数のドロップダウンがあり、フォーム内の値が相互に依存しているWebページをスクレイプしようとしています。多くの場合、ページの更新が完了するまで待つコードが必要です。たとえば、リストからオプションを選択した後、コードはこの選択に基づいて次のリストが入力されるまで待機する必要があります。不思議なことに、コードが動作するのは、不必要なロギングステートメントを与えてから遅延が発生したためです。コードを改善するための提案は非常に役立ちます。
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();
私はここでArunが言及したwaitForSelector「回避策」を使用しています: https://stackoverflow.com/a/22217657/18420
それは私が見つけた最高のソリューションです。本来の「欠点」は、どの要素をロードすることを期待しているのかを認識する必要があるということです。私は欠点を言いますが、個人的には、someのようなフィードバックがなかったという状況に遭遇したとは思いません。待っています
this.waitForSelector("{myElement}",
function pass () {
test.pass("Found {myElement}");
},
function fail () {
test.fail("Did not load element {myElement}");
},
20000 // timeout limit in milliseconds
);
視覚的なフィードバックがなければ、 waitForResource() などを使用できると思いますが。
ターゲットに固有のものがなく、リロードされたページで待機するときにこの問題を回避するために私がやったことは、以下を使用することです:
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 は利用可能な回避策であり、常に機能するとは限りません。
私はあなたと同じことをして同じ経験をしています。ユーザーの視点でこれらの方法でスクリプトを作成してもうまくいきません。それはどこかでクラッシュし、非常に信頼できません。ログインが必要なセールスフォースから検索していました。
ステップをできるだけ最小限に抑える必要があります。 cronジョブの方法でスクリプトを作成します。 UIテストを行っていない限り、フォームの入力/ボタンのクリックは行わないでください。プロセスを2つの部分に分けることをお勧めします
// 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)
私のスクリプトは純粋なファントムjsです。キャスパースクリプトは理由もなくクラッシュし続けます。 phantomjsでさえも信頼できません。検索/ダウンロードが成功するたびに結果/ステータスを保存します。エラーが発生するたびに、結果の残りが予測不能でない場合はスクリプトを終了します(chrome phantomjsで悪い結果になる)。
Click()またはfill()アクションが子iframeでまったく同じデータをリロードする問題の解決策を検索するときに、この質問を見つけました。 Pebbl answerの改善点は次のとおりです。
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は開発者向けに作成されているため、ロードされたページの状態と、ページがロードされた状態を定義するために使用できる要素を知っていることが期待されます。
1つのオプションは、たとえば、ページの最後にロードされるjavascriptリソースの存在を確認することです。
あらゆるタイプのテストを実行する場合、結果は毎回再現可能でなければならないため、べき等性が不可欠です。これが起こるためには、テスターはこれを実現するのに十分な環境を制御できなければなりません。
評価するだけ document.readyState
はcomplete
またはinteractive
になります。それからロードされます。
これはwhile
を使用した実装ですが、おそらく間隔を空けて実行できます...
this.then(function () {
while(this.evaluate(function () { return document.readyState != 'complete' && document.readyState != 'interactive'; })) {}
});