numbersでエクセルのwebクエリのような機能はありますか?
webのデータを自動取得をしたいのですが、numbersでできる方法がありますか?
MacBook Air (11-inch, Mid 2012), OS X Mountain Lion (10.8.2)
webのデータを自動取得をしたいのですが、numbersでできる方法がありますか?
MacBook Air (11-inch, Mid 2012), OS X Mountain Lion (10.8.2)
ブラウザ上で選択したテーブルの中身を CSV/TSV で出力...ですが、Camino だと選択部分の HTML ソースが AppleScript で取得できるので、比較的簡単にできそうです。
↓ 表を選択
↓ AppleScript で選択部分の HTML ソースを取得
↓ CSV/TSV に加工
↓ 出力
# Camino は開発が終了してしまったので、おすすめできないのが、最大の欠点...。
ーーーーー
別案としては Safari + Automator サービスを使った方法。
↓ Bookmarklet か do JavaScript からテーブル番号を挿入
↓ その番号を選択して Automator サービス実行 (テキストを受け取る)
↓ CSV/TSV に加工 (do JavaScript)
↓ 出力
bookmarklet はこんな感じ (長いけど1行です)
javascript:(function(){var tables=document.getElementsByTagName('table');for(i=0;i<tables.length;i++){var div=document.createElement('div');div.innerHTML=i;div.style.backgroundColor='lightgreen';var table=document.getElementsByTagName('table').item(i);table.parentNode.insertBefore(div,table);}})();
# Automator サービスで選択したテキストを取得して...とするのが最も簡単なのですが、セル内改行があると期待どおりにデータが取得できないので、こんな面倒なことに...。
# ワンタッチではないので、ちょっとかっこ悪いけど、まあ我慢できるかなと。というか、JavaScript で全部やろうとしたけどできませんでした。
ーーーーー
あと、決まったデータを定期的に取得する場合は、ブラウザを使わずにスクリプト言語で直接 HTML ソースを取得→解析→出力する方法もあります。
HTML の解析ツールはいろいろありますが、PHP の Simple HTML DOM Parser が使いやすいと思います。AppleScript から使う場合は、スクリプトバンドル形式にして、バンドル内に突っ込んじゃうと良いでしょう。
Simple HTML DOM Parser
http://sourceforge.net/projects/simplehtmldom/
度々、失礼致します。 do JavaScriptコマンドを実行して定義した変数は、Safari?に保持されているようなので、再度、do JavaScriptコマンドを実行する際にそのまま使うことが出来る様です。 なので、マウスクリックで表を選択するイベントを付け加えてみました。
tell document 1 of application "Safari"
activate
-- 表にidが無ければidを付ける
set theScript to "
var tables=document.getElementsByTagName('table');
for(i=0;i<tables.length;i++)
{
if(tables[i].id==='')
{
tables[i].id='tbl_'.concat(i.toString());
}
}"
do JavaScripttheScript
-- 表のデータ(TSV)を返す関数を定義
set theScript to "
var theData =function(tableId){
var table=document.getElementById(tableId);
var str='';
var rows=table.rows;
for(r=0;r<rows.length;r++){
var cells=rows[r].cells;
for(c=0;c<cells.length;c++){
str=str+cells[c].textContent.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm,''); // テキスト中の改行やタブを取り除く
if(c<cells.length-1){str=str+'\\t'};
};
if(r<rows.length-1){str=str+'\\n'};
};
return str;
}
"
set theData to do JavaScripttheScript
--- 表にマウスオーバーイベントを付ける(枠の強調表示と背景色の変化、TSVデータのタイトル表示)
set theScript to "
for(i=0;i<tables.length;i++)
{
tables[i].onmouseover=function(){
this.style.backgroundColor='lightgreen'; // 背景色の設定
this.style.border='medium solid #0000EF'; // 枠の強調
};
tables[i].onmouseout=function(){
this.style.backgroundColor=''; // 背景色の解除
this.style.border=''; // 枠の解除
};
tables[i].title=theData(tables[i].id); // タイトルにデータを設定
}"
do JavaScripttheScript
--- 表にクリックイベントを付ける(クリックした表のidを変数_theIdに保存)
set theScript to "
var theId='';
for(i=0;i<tables.length;i++)
{
tables[i].onclick=function(){theId=this.id}
}
theId"
set theId to do JavaScripttheScript
-- クリックイベントを待つ
do JavaScript "alert('選択する表をクリックしてください。\\n\\n表にマウスを載せると強調表示されます。')"
try
repeat while (theId is "")
delay 1
set theId to do JavaScript "theId"
end repeat
set theId to quoted form of theId
set theData to do JavaScript "document.getElementById(" & theId & ").title"
-- クリップボードにデータを保存
set the clipboard totheData
do JavaScript "alert('データをクリップボードに保存致しました。')"
on error
do JavaScript "alert('処理を中断致しました.')"
end try
do JavaScript "location.reload()" # ページを再ロード
end tell
Numbersに読む込む操作や、自動的に表の内容を更新する操作は、今後検討していきます。
現在のヴァージョンのNumbersには同等の機能はないと思います。 しかし、AppleScriptやAutomatorの機能と連携させると可能かもしれません(私は試行錯誤の段階で止まっています)。 ちなみにどんなデータなのでしょう?
do JavaScriptコマンドを実行して定義した変数は、Safari?に保持されているようなので、再度、do JavaScriptコマンドを実行する際にそのまま使うことが出来る様です。
ここに引っかかって、過去の自分のスクリプトを眺めてみたところ、重要なことを思い出しました。
do JavaScript だと、変数がグローバルになってしまうようなので、同名変数が既にある場合にちょっとまずいことになる可能性があります。変数汚染問題ってやつです。
これは無名関数にすると回避できますが、そうすると、値を返すことができなくなります。また、ページをリロードすればすべて御破算になりますが、ネットショッピング中とかはやるべきではありません。
根本的にひっくり返すような話で申し訳ありません。もっと早く思い出すべきでした。
ーーーーー
Bookmarklet - ブックマークレットのグローバル変数汚染デモ
http://www.teria.com/~koseki/memo/bookmarklets/blet_global.html
例えば、「表にクリックイベントを付ける(クリックした表のidを変数_theIdに保存」の変数名「i」を「n」にして、上記ページで試すと、クリックしても何も起こらなくなります。
もうちょっと分かりやすい例だとこんな感じ。
-- スクリプトA
tell document 1 of application "Safari"
do JavaScript "
document.body.style.color='green'
var n = 5;
n
"
end tell-- スクリプトB 無名関数
tell document 1 of application "Safari"
do JavaScript "
(function() {
document.body.style.color='green'
var n = 20;
n
})();
"
end tell検証手順
1. スクリプトA を実行した後で「アラート表示」ボタンを押す
2. ページをリロード
3. スクリプトB を実行した後で「アラート表示」ボタンを押す
アラート回数と値の取得状況を比べると良く分かると思います。
> delay 1
ここだけ。下のリンク先にある openWithWait() を使うと、ページの読み込み完了まで待ってくれます。Safari 最新版でちゃんと動くか分かりませんが、当方 (Safari 5.1.10) では大丈夫です。
applescript: Safariでページロードを待つ処理
http://tezfm.blogspot.jp/2009/10/applescript.html
AppleScriptでSafariのwebarchiveを保存したい
素晴らしい!感動しました!
Numbersに読む込む操作
ええと、CSV/TSV と書いておいてなんですが...、最初のスクリプトみたいに配列で渡してはどうでしょうか。実は Numbers のことが完全に頭から抜けてました...。何か申し訳ない...。
例えばこんな感じで行毎の多次元配列にしてみてはどうでしょうか?下手に改行を削ると、1\n2 → 12 みたいになっちゃうので、配列で渡せるならそうした方が安全かと。Numbers を使ってないので、どのようなカタチで渡せば良いか分かりませんが、一応参考まで。
-- 富士山
on run
set n to 4
my parse_table(n)
end run
--
on parse_table(n)
tell document 1 of application "Safari"
do JavaScript ("
var table = document.getElementsByTagName('table')[" & n & "]
var ary1 = []
for (var r = 0; r < table.rows.length; r++) {
var ary2 = []
for(var c = 0; c < table.rows[r].cells.length; c++) {
data = table.rows[r].cells[c].innerHTML
//ここでテキスト処理
ary2.push(data)
}
ary1.push(ary2)
}
ary1
")
end tell
end parse_tableHiro.S 様、
重要な情報を頂きまして、有り難うございます。 ページのJavaScriptで定義されているグローバル変数と同じ名前をAppleScript側で再定義してしまうと、本来のページの機能が損なわれてしまう訳ですね。 tablesとか、tableとか、rowsとか、theData、theIdなど、いかにも重複しそうな変数名を使うのは駄目ですね。 他の変数名を使うにしても、重複していないかどうか確かめる処理が必要になるので、複雑になりそうです。 もう少し考えてみます。 とても参考になりました。 改めて、御礼申し上げます。
度々、失礼致します。 WebページのJavaScriptで定義されているグローバル変数の一覧を含むプロパティをObject.keys(windows)で取得できるようです。なので、予め変数に重複があるかどうか調べることができました。
tell document 1 of application "Safari"
set theScript to "Object.keys(window)"
set theGVs to (do JavaScripttheScript)
set theKeywords to {"tables", "theData", "theId"}
repeat with theWord in theKeywords
if (theGVs contains theWord) then
display alert ("このページにはグローバル変数 " & quoted form of theWord & " を含んでいます。¬
do JavaScriptで定義する変数名を変える必要があります。")
end if
end repeat
end tell
少し手間ですが、変数汚染を回避する良い方法がみるかるまで、ひとつひとつのWebページでグローバル変数を調べる必要がありそうです。
Hiro.S 様
有り難うございます。 そうですねdelayコマンドで適当な時間を設定するよりも、ページが完全に読み込まれるまで待つ様にした方が良いですね。 リンク先のAppleScriptを勉強してみます。 有用な情報を頂きまして、ありがとうございました。
numbersでエクセルのwebクエリのような機能はありますか?