CasperJSを使用して、Webサイトを通じて一連のクリック、完成したフォーム、データの解析などを自動化します。
Casperはthen
ステートメントの形式のプリセットステップのリストに整理されているようです(ここの例を参照してください: http://casperjs.org/quickstart.html )実際に実行される次のステートメントをトリガーするもの。
たとえば、then
は、保留中のすべての要求が完了するまで待機しますか? injectJS
は保留中のリクエストとしてカウントされますか? then
ステートメントがネストされている場合-open
ステートメントの最後にチェーンされている場合はどうなりますか?
casper.thenOpen('http://example.com/list', function(){
casper.page.injectJs('/libs/jquery.js');
casper.evaluate(function(){
var id = jQuery("span:contains('"+itemName+"')").closest("tr").find("input:first").val();
casper.open("http://example.com/show/"+id); //what if 'then' was added here?
});
});
casper.then(function(){
//parse the 'show' page
});
CasperJSでフローがどのように機能するかの技術的な説明を探しています。私の特定の問題は、最後のthen
ステートメント(上記)がcasper.open
ステートメントの前に実行されることです。その理由はわかりません。
then()
は、基本的にスタックに新しいナビゲーションステップを追加します。ステップは、2つの異なることを実行できるjavascript関数です。
簡単なナビゲーションシナリオを見てみましょう。
_var casper = require('casper').create();
casper.start();
casper.then(function step1() {
this.echo('this is step one');
});
casper.then(function step2() {
this.echo('this is step two');
});
casper.thenOpen('http://google.com/', function step3() {
this.echo('this is step 3 (google.com is loaded)');
});
_
次のように、スタック内に作成されたすべてのステップを印刷できます。
_require('utils').dump(casper.steps.map(function(step) {
return step.toString();
}));
_
それは与える:
_$ casperjs test-steps.js
[
"function step1() { this.echo('this is step one'); }",
"function step2() { this.echo('this is step two'); }",
"function _step() { this.open(location, settings); }",
"function step3() { this.echo('this is step 3 (google.com is loaded)'); }"
]
_
URLを読み込むためにCasperJSによって自動的に追加された_step()
関数に注目してください。 URLがロードされると、スタックで使用可能な次のステップ(step3()
)が呼び出されます。
ナビゲーション手順を定義したら、run()
はそれらを1つずつ順番に実行します。
_casper.run();
_
脚注:コールバック/リスナーは Promiseパターン の実装です。
then()
は単に一連のステップを登録するだけです。
run()
とそのランナー関数、コールバック、およびリスナーのファミリーはすべて、各ステップを実行する作業を実際に行うものです。
ステップが完了するたびに、CasperJSは3つのフラグに対してチェックします:pendingWait
、loadInProgress
、およびnavigationRequested
。これらのフラグのいずれかが真である場合、何もせず、後でアイドル状態になります(setInterval
スタイル)。これらのフラグのいずれも真でない場合、次のステップが実行されます。
CasperJS 1.0.0-RC4の時点で、特定の時間ベースの状況下で、CasperJSがloadInProgress
またはnavigationRequested
フラグ。解決策は、これらのフラグのいずれかを上げてから、それらのフラグが立てられると予想されるステップを離れることです(例:casper.click()
を要求する前後にフラグを立てます)。
(注:これは単なる例であり、適切なCasperJS形式よりも擬似コードに似ています...)
step_one = function(){
casper.click(/* something */);
do_whatever_you_want()
casper.click(/* something else */); // Click something else, why not?
more_magic_that_you_like()
here_be_dragons()
// Raise a flag before exiting this "step"
profit()
}
そのソリューションを1行のコードにまとめるために、このgithubにblockStep()
を導入しました プルリクエスト 、click()
およびclickLabel()
をthen()
を使用するときに期待される動作が得られることを保証することを意味します。詳細、使用パターン、最小限のテストファイルについては、リクエストをご覧ください。
CasperJS Documentation :によると
then()
署名:then(Function then)
このメソッドは、単純な関数を提供することにより、スタックに新しいナビゲーションステップを追加する標準的な方法です。
casper.start('http://google.fr/');
casper.then(function() {
this.echo('I\'m in your google.');
});
casper.then(function() {
this.echo('Now, let me write something');
});
casper.then(function() {
this.echo('Oh well.');
});
casper.run();
必要な数のステップを追加できます。現在の
Casper
インスタンスは、ステップ関数内でthis
キーワードを自動的にバインドします。定義したすべてのステップを実行するには、
run()
メソッドを呼び出してください。注:
start()
メソッドを使用するには、then()
を使用する必要があります。警告:
then()
に追加されたステップ関数は、2つの異なるケースで処理されます。
- 前のステップ関数が実行されたとき、
- 前のメインHTTP要求が実行され、ページがロードされたとき;
page loaded;の単一の定義はないことに注意してください。 DOMReadyイベントがトリガーされたときですか? 「すべてのリクエストが終了しています」ですか? 「実行されているすべてのアプリケーションロジック」ですか?または「すべての要素がレンダリングされます」?答えは常にコンテキストに依存します。したがって、常に
waitFor()
ファミリーメソッドを使用して、実際に期待することを明示的に制御することが推奨される理由です。一般的なトリックは、
waitForSelector()
を使用することです。
casper.start('http://my.website.com/');
casper.waitForSelector('#plop', function() {
this.echo('I\'m sure #plop is available in the DOM');
});
casper.run();
シーンの背後で、 Casper.prototype.then
のソースコード を以下に示します:
/**
* Schedules the next step in the navigation process.
*
* @param function step A function to be called as a step
* @return Casper
*/
Casper.prototype.then = function then(step) {
"use strict";
this.checkStarted();
if (!utils.isFunction(step)) {
throw new CasperError("You can only define a step as a function");
}
// check if casper is running
if (this.checker === null) {
// append step to the end of the queue
step.level = 0;
this.steps.Push(step);
} else {
// insert substep a level deeper
try {
step.level = this.steps[this.step - 1].level + 1;
} catch (e) {
step.level = 0;
}
var insertIndex = this.step;
while (this.steps[insertIndex] && step.level === this.steps[insertIndex].level) {
insertIndex++;
}
this.steps.splice(insertIndex, 0, step);
}
this.emit('step.added', step);
return this;
};
説明:
つまり、then()
は、ナビゲーションプロセスの次のステップをスケジュールします。
then()
が呼び出されると、ステップとして呼び出されるパラメーターとして関数が渡されます。
インスタンスが開始されたかどうかを確認し、開始されていない場合は、次のエラーを表示します。
CasperError: Casper is not started, can't execute `then()`.
次に、page
オブジェクトがnull
であるかどうかを確認します。
条件が真の場合、Casperは新しいpage
オブジェクトを作成します。
その後、then()
はstep
パラメーターを検証して、それが関数でないかどうかを確認します。
パラメータが関数ではない場合、次のエラーが表示されます。
CasperError: You can only define a step as a function
次に、関数はCasperが実行されているかどうかを確認します。
Casperが実行されていない場合、then()
はキューの最後にステップを追加します。
それ以外の場合、Casperが実行されている場合、前のステップよりも深いレベルのサブステップを挿入します。
最後に、then()
関数は step.added
イベントを発行して終了し、Casperオブジェクトを返します。