AlfredのWorkflowでWebサイトのDOMを操作したりする

とあるサイトの検索フォームをAlfredから直接実行したかった。検索用のAPIが用意されていないので、ページのformからsubmitしないといけない。

偶然、AppleScriptからSafariのページでJavaScriptを実行できる事を知ったので、以下のような流れで実現できると思い、調査開始。

  1. Alfredで検索文字列をAppleScriptに渡す
  2. AppleScriptで、
    1. Safariに検索ページを表示
    2. do JavaScript でDOM操作(formに入力してsubmit)

今回はじめてAppleScriptに挑戦し独特な文法に困惑していたところ、JavaScript (Java Script for Automation; JXA) でも書ける事がわかって、そちらに避難した。プログラミングが初めてならApple Scriptの方が英語の文法そのままなので分かりやすいのかもしれないけど、、AppleScriptはまた次の機会に。

Alfredで検索文字列をApple Scriptに渡す

Templates > Essentials > Keyword to Script を選び、名前やキーワードを適当に設定する。ScriptのLanguageに /usr/bin/osascript (JS) を選ぶ。

f:id:naokton:20201221114423p:plain f:id:naokton:20201221114901p:plain f:id:naokton:20201221114935p:plain

スクリプト

Workflowの前ステップから渡ってきたキーワードを {query} 経由で受け取って処理を実行する。以下は例としてWikipediaのトップページから検索するようになっている。

function run(){
    let url = 'https://en.wikipedia.org/wiki/Main_Page'
    let word = "{query}"
    let app = Application("Safari")
    app.includeStandardAdditions = true

    app.activate()
    let tab = loadUrlNewTab(app, url)
    waitPageLoad(app, tab)
    searchWord(app, tab, word)
}

function loadUrlNewTab(app, url){
    let curWin = app.windows[0]
    curWin.tabs.push(app.Tab())
    curWin.currentTab = curWin.tabs[-1]
    curWin.currentTab.url = url
    return curWin.currentTab
}

function waitPageLoad(app, tab){
    ret = false
    do {
        ret = app.doJavaScript(
            "(function(){return document.readyState == 'complete'})()",
            { in: tab }
        )
        delay(0.2)
    }while(ret==false)
}

function searchWord(app, tab, word){
    app.doJavaScript(
        "(function(){\
            let form = document.getElementById('searchform');\
            let input = document.getElementById('searchInput');\
            input.value = '" + word + "';\
            form.submit();\
        })()",
        {in: tab}
    )
}

作るときは、Script Editorで実行(Cmd + R)して動きを確認しながら書いていった。

loadUrlNewTab はもう少し簡潔に書けるような気がするけど、とりあえず愚直に。

waitPageLoad は、これがないとページ読み込み完了より先にDOMを読みに行ってしまうので必要。

searchWord 中の getElementById は、ページに合わせて適当に修正する。

使い方

キーワード スペース 検索語句 -> Enter でタブが開いて、検索結果が表示される。 f:id:naokton:20201221122624p:plain

参考

ところでosascriptで使えるAppleScriptJavaScriptも、あまり流行っていないのか情報が少なくて苦労した。流行りの言葉だと、どうやって書くんだろうと思って調べると、大抵StackOverflowなどでやりたい事が既に質問されていたりするのだけど、それがない。ただし大変ありがたいことに、情報をまとめてすばらしい解説ページを作ってくれている方々がいたおかげで、AppleScript / JXA がどんな感じかなんとなく掴む事ができた。ありがたやありがたや。