それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Safariのページロードを待つ( Javscript for OSX Automation )

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

これを実行すると、次のようになります

14

ちゃんとページ内スクリプトであることを確認します。

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()しました。

15

ページ内で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タグそのものは非同期に実行されないので、を作ればなんとかなると思うけど。

jQueryのロードチェックが必要になったことがまだないので、そのうち考えます。ごめんなさい。。

今回のまとめ:doJavaScriptでSafariにインジェクション出来る

app = Application('Safari')
app.includeStandardAdditions = true
app.activate()
var tab = app.windows[0].tabs[0]
tab.url = "http://example.com"


//ページ内でJSを実行する
app.doJavaScript( "alert()"  , { in: tab } )//evalさせたい文字列

これですね。まぁ tab.url = "javascript:alert()"でも同じだと思ったんだけど url = "javascript:" は受け付けてくれないんですよね。