kurukuru-papaのブログ

主に、ソフトウェア開発に関連したメモを書き溜めたいと思います。

AppleScriptでSafari上のフォーム入力を自動化してみた

MacのSafariで、多数のデータ入力を行う機会がありました。一つの画面で、繰り返し同じような入力を何度も行う作業です。自動化向きの作業だと感じましたので、いくらか試行錯誤し、AppleScriptで半自動化することにしました。

「半自動化」です。「完全自動化」ではありません。1件入力するごとに、画面表示内容などを確認しながら作業を進めたかったので、1件自動入力後、ユーザの応答待ちとなり、問題なければ、次の1件を自動入力するようにしました。

なお、AppleScriptは今回はじめて作成しました。なんとか私の環境では動作するレベルになりましたが、お作法的に正しい作りになっているのかなどは全く自信無いです。綺麗に作られているとは、夢にも期待しないで下さい。

AppleScirptによる半自動化 実施手順

操作方法は、こんな感じになります。

1.自動入力データを作成

後述するように自動入力用のデータファイルを作成して下さい。ファイルの場所は、スクリプトファイルと同じフォルダで、ファイル名を「data.txt」として下さい。
※現状ではファイル名固定にしてあります。

2.テスト用ページを表示

Safariで、自動入力を行いたいページを表示して下さい。
Safariでは複数のウインドウを立ち上げたり、複数のタブを開いたりしないようにお願いします。

3.スクリプトファイルautoinput.scptを実行

私の作成した「autoinput.scpt」を、スクリプトエディタで開き、ウインドウ上部にある「実行」ボタンを押して下さい。Safariが前面表示され、データが自動入力されます。1件分の自動入力が終わると、2件目の自動入力を続行するかダイアログが表示されます。続行ボタンを押すと、2件目入力が始まります。
自動入力がすべて完了すると、完了ダイアログが出てきて終了です。

AppleScriptによる半自動化 スクリプト内容

3つのスクリプトファイルから構成されています。

全体を制御するスクリプト autoinput.scpt
--外部ファイルの呼び出し
property fileModule : null
property formModule : null
tell application "Finder"
	set basePath to parent of (path to me) as text
end tell
set fileModule to load script file (basePath & "file.scpt")
set formModule to load script file (basePath & "form.scpt")

--テスト用ファイルパス
--とりあえずファイル名固定にしてあります。
set filePath to basePath & "data.txt"

tell application "Safari"
	activate
	
	--入力データ読み込み
	tell fileModule
		set commandArr to getCommandArrFromFile(filePath)
	end tell
	
	--Safari上のフォームへデータ入力を行う
	tell formModule
		play(commandArr)
	end tell
	
	activate
	display dialog "完了!" buttons {"OK"} default button 1
end tell
入力データを読み込むスクリプト file.scpt
property TAB_CHAR : ASCII character (9)
property CR_CHAR : ASCII character (10)
property LF_CHAR : ASCII character (13)

--ファイル読み込み
--引数のファイルを読み込み、コマンドごとに分割した配列を返却します。
on getCommandArrFromFile(filePath)
	--ファイルを読み込む
	try
		set fileNum to open for access file filePath without write permission
		set allText to read fileNum
	on error
		activate
		display dialog "Error!" & filePath buttons {"Cancel"} default button 1
	end try
	close access fileNum
	
	--読み込んだファイル内容を改行で分割する
	set orgDelim to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {CR_CHAR, LF_CHAR}
	set lineArr to text items of allText
	set AppleScript's text item delimiters to orgDelim
	
	--行をカンマで分割する
	set commandArr to {}
	repeat with aLine in lineArr
		if length of aLine > 0 then
			set orgDelim to AppleScript's text item delimiters
			set AppleScript's text item delimiters to {","}
			set stringArr to text items of aLine
			set AppleScript's text item delimiters to orgDelim
			set the end of commandArr to stringArr
		end if
	end repeat
	
	return commandArr
end getCommandArrFromFile
Safariへの自動入力を行うスクリプト form.scpt
property RETURN_CHAR : ASCII character 3 --Return
property TAB_CHAR : ASCII character 9 --TAB
property CR : ASCII character 13
property UP_ARROW_CHAR : ASCII character 30 --上
property DOWN_ARROW_CHAR : ASCII character 31 --下
property SPACE_KEY : 49 --スペースキー

on delayL()
	delay 1
end delayL

on delayM()
	delay 0.1
	--delay 0.5 --DEBUG 動作をゆっくり確認したい場合コメントインする
end delayM

--Safariのロケーションバーへフォーカス
--フォーカスの位置合わせのために使います。
on selectLocation()
	tell application "System Events"
		tell application process "Safari"
			delayM() of me
			keystroke "l" using command down
		end tell
	end tell
end selectLocation

--フォームのテキストボックスへ入力
on inputText(param)
	tell application "System Events"
		tell application process "Safari"
			delayM() of me
			keystroke "a" using command down
			delayM() of me
			set the clipboard to param
			keystroke "v" using command down
		end tell
	end tell
end inputText

--フォームのテキストエリアへ入力
on inputTextArea(param)
	inputText(param)
end inputTextArea

--フォームのプルダウンを選択
on inputSelect(param)
	tell application "System Events"
		tell application process "Safari"
			--選択状態を初期化するため、プルダウンの先頭項目を選択する
			delayM() of me
			keystroke UP_ARROW_CHAR --上
			keystroke UP_ARROW_CHAR using command down --Command+上
			--引数で指定された項目を選択する
			delayM() of me
			repeat param - 1 times
				keystroke DOWN_ARROW_CHAR --下
			end repeat
			keystroke RETURN_CHAR --Returnキー
		end tell
	end tell
end inputSelect

--フォームのチェックボックスを選択
on inputCheckbox(param)
	tell application "System Events"
		tell application process "Safari"
			--チェックボックスの選択状態を初期化したいが方法がわからない
			--チェックをつける
			if param = "true" then
				delayM() of me
				key code SPACE_KEY --スペースキー
			end if
		end tell
	end tell
end inputCheckbox

--フォームのラジオボタンを選択
on inputRadio(param)
	tell application "System Events"
		tell application process "Safari"
			--ラジオボタンの選択状態を初期化したいが方法がわからない
			delayM() of me
			repeat param - 1 times
				keystroke UP_ARROW_CHAR --上
			end repeat
			--引数で指定された項目を選択する
			delayM() of me
			repeat param - 1 times
				keystroke DOWN_ARROW_CHAR --下
			end repeat
		end tell
	end tell
end inputRadio

--ダイアログを表示
--ユーザからの応答を待ち、自動入力の続行、停止を選択できます。
on displayDialog(msg)
	set resultFlag to false
	tell application "System Events"
		tell application process "Safari"
			display dialog msg buttons {"停止", "続行"} default button 2
			if button returned of result = "続行" then
				set resultFlag to true
			end if
		end tell
	end tell
	return resultFlag
end displayDialog

--フォームのフォーカスを次項目に移動
on nextItem()
	tell application "System Events"
		delayM() of me
		keystroke TAB_CHAR using option down --Option+TAB
	end tell
end nextItem

--データ入力1件を実施
on playOneCommand(stringArr)
	set resultFlag to true
	set type to first item of stringArr
	set value to last item of stringArr
	
	if type = "location" then
		selectLocation()
	else if type = "delay" then
		delayL()
	else if type = "next" then
		nextItem()
	else if type = "text" then
		inputText(value)
		nextItem()
	else if type = "textarea" then
		inputTextArea(value)
		nextItem()
	else if type = "select" then
		inputSelect(value)
		nextItem()
	else if type = "checkbox" then
		inputCheckbox(value)
		nextItem()
	else if type = "radio" then
		inputRadio(value)
		nextItem()
	else if type = "dialog" then
		displayDialog(value)
		set resultFlag to result
	else
		log "不明なコマンドです。「" & stringArr & "」"
	end if
	
	return resultFlag
end playOneCommand

--入力データ全件の実施制御を行う
on play(commandArr)
	set resultFlag to true
	repeat with stringArr in commandArr
		log stringArr
		
		tell application "Safari" to activate
		playOneCommand(stringArr)
		set resultFlag to result
		if resultFlag = false then
			exit repeat
		end if
	end repeat
	
	return resultFlag
end play

その他の自動化案 Selenium

Seleniumを使用して自動化するサンプルを作成してみたのですが、テキストボックスのvalue属性に直接値を設定すると、入力値を上手く扱えなくなってしまいました。対象のWebアプリケーションは、jQueryを多用して作られていて、テキストボックスのvalue属性は、ユーザが値を入力したときには入力値を表示し、ユーザ未入力時は入力項目の説明(ウォーターマーク?というのかな)を表示していました。value属性に2つの用途をもたせたアプリなので、Seleniumから直接value属性を設定すると問題があるのかもしれません。

ダウンロード

今回作成したファイルは、次からダウンロードできます。ファイル名は「safari002-XXX.zip」(XXX部分はバージョン番号)です。

動作環境

  • Mac OS X 10.5, 10.7
  • Safari 5.0.6, 5.1
  • 自動入力対象のサンプルページを作成するためにCoolWebWindowさんの素材を使わせて頂きました。どうもありがとうございます。