Safariを自動操縦のその2です。
前回Safariを起動して、ページ遷移をしました。
そのさい、ページのロードを、スリープして待っていただけでした。しかし。今回はページのロードをJavaScriptを使って待とうと思います。
前回の記事はこちら⇛Safariの自動操縦 (JXA: JavaScript for OSX Automation) - それマグで!
前回のスクリプト
app = Application('Safari') app.includeStandardAdditions = true app.activate() var tab = app.windows[0].tabs[0] tab.url = "http://pixiv.net"
SafariのApplicationをAutomationにロードして、Windowをとりだしタブを取り出し、ページをロードしています。
前回のスクリプトの補足説明:コレクション
AppleScriptの自動化では、アプリは次のような階層構造を持ちます。
Application ⇛ Window ⇛ tab ( Document )
階層構造を持つものは、子要素はコレクションとして「複数形」でアクセスします。⇐ここ重要
Windowを取り出すときに、複数形から取り出します。
Application("Safari").windows //Windowオブジェクトの配列
複数形のアクセスは大事なので覚えておきます。
この辺のところは、タブの管理やDocumentの管理でまた書きます。(たぶんリマインダ.app くらいが分かりやすいと思います)
Safariのタブを取り出して、URLにアクセスする。
JavaScriptで、次のようにかくと、URLにアクセスすることが出来ます。
app = Application('Safari') app.includeStandardAdditions = true app.activate() var tab = app.windows[0].tabs[0] tab.url = "http://pixiv.net"
このタブの中で、JavaScript実行しようと思います。
doJavaScriptコマンドをつかって、ページ内でJSを実行
app = Application('Safari') app.includeStandardAdditions = true app.activate() var tab = app.windows[0].tabs[0] tab.url = "http://pixiv.net" //ページ内でJSを実行する app.doJavaScript( "alert()" , { in: tab } )
これを実行すると、次のようになります
ちゃんとページ内スクリプトであることを確認します。
app = Application('Safari') app.includeStandardAdditions = true app.activate() var tab = app.windows[0].tabs[0] tab.url = "http://pixiv.net" //ページ内でJSを実行する app.doJavaScript( "(" + ( function(){ alert(document.title+'からこんにちは') } ).toString()+")()" , { in: tab } )
ページのタイトルをAlert()しました。
ページ内でJavaScriptを実行できました
これをつかって、ロード待ちをします。
JavaScriptでロード待ちの定番といえば、
document.readyState == "complete" ; //このチェックですね
このチェックとスリープがあれば、それなりにはかどります。
app = Application('Safari') app.includeStandardAdditions = true app.activate() var tab = app.windows[0].tabs[0] tab.url = "http://pixiv.net" //ページ内でJSを実行する ret = false do { ret = app.doJavaScript( "(function(){ return document.readyState == 'complete' })()", { in: tab } ) delay(0.2) }while(ret==false) console.log("ロード完了")
これで、ページ読み込み完了をまって処理をすることが出来ますね。
お気づきでしょうか。ページ内JavaScriptから trueが返ってくると、AppleScript側でもtrueとして処理できるのです。
result = app.doJavaScript( "(function(){ return document.readyState == 'complete' })()", { in: tab } ) result //=> false
すべてをJSで書けるというのはほんとうに嬉しい限りです。
あとは、これを関数にして。
このローディングを待つ機能を、関数にして、使いまわせるようにする。
function wait_page_load(tab){ app = Application('Safari') ret = false do { ret = app.doJavaScript( "(function(){ return document.readyState == 'complete' })()", { in: tab } ) delay(0.2) }while(ret==false) } app = Application('Safari') app.includeStandardAdditions = true app.activate() var tab = app.windows[0].tabs[0] tab.url = "http://pixiv.net" wait_page_load(tab) //ページ内でJSを実行する console.log("ロード完了")
関数の作り方も、通常のJSと同じ
ここが嬉しいんだけど、通常のJSと同じで、関数を作ることが出来る。
ChromeのExtensionやHTML DOM APIと違って、非同期のコールバック地獄がないので、比較的楽に使うことが出来ます。
jQuery のロード待ちに対応する。
jQuery を使っているサイトだと、document.ready ではチャントチェックが出来ずにHTMLの取得に失敗することがある。
その時は、、、うーん。。。とりあえずdelay()
jQuery の $.ready に自前の関数を突っ込んだら末尾に入るでしょ、関数は追加順に実行されるので、自分の関数が実行されたら、最後ってことがわかかるはずだから。
これをつかって、document.body の終了タグの直前に appendchildしてやれば、Scriptタグそのものは非同期に実行されないので、