序
最近努力學習新知識,在公司內網上閱讀一些優秀文章,但是發現這些文章:是通過通過flash控件展示,並且不提供無法下載地址(類似《百度文庫》)。一次忍了,兩次再忍了,三次就無法忍受:都在我電腦上展示了,這麼做就是噁心人了。。。。
於是花了大半天時間,使用mac上自帶軟件Automator,從零開始完成了自己的工具:《連續抓圖導出PDF》。
實現原理:自動截屏、翻頁=》merge生成PDF文檔。(感興趣的人,可以下載附件看看源碼)
在寫這個腳本過程中,本以爲會很快,但是還是碰到了不少問題;於是就記錄並分享出來。
首先:最基本用法,可以直接參考Automator幫助,本文章只寫自認爲容易忽略或者難點的幾個地方。
變量使用
變量新建就不說了;這裏主要說變量再控件上使用,幾種方法(有的方法只對部分控件有用):
從下拉列表直接選擇“變量”
將“變量”拖進區域
當然你也可以,拋棄使用已存在的變量,直接通過下拉框選擇一個“新建變量”。
“輸入變量名”彈出提示,“回車”選擇
最後,如果上面三種方法你嘗試都不行的話,基本上可以宣告,那個是不支持變量的。
比如,LOOP的“次數”,是不支持變量的:
腳本
腳本使用
Automator腳本支持:shell腳本(bash、perl、python、ruby等)、appleScript、javascript幾種,你可以選擇不同的腳本來運行。
腳本參數傳遞
腳本和變量之間,是無法直接獲取或設置的
“變量”值傳遞到腳本
– shell腳本
獲取到的輸入:只能是通過輸入的一行一行的字符串
也即如果,想獲取變量值的話,可以通過,“獲得變量的值”控件來輸出值,然後傳遞到腳本然後再讀取。
如:下面是獲取“頁數”、“URL”、“臨時文件夾”三個變量值到輸出,然後傳遞給perl腳本。
– AppleScript腳本
AppleScript的輸入不是一行一行的字符串,貌似是鍵值隊(還不熟悉這塊),結果沒搞懂如何傳遞的變量——搞懂後,再補充。
於是我考慮的解決方案是:通過分隔符“|”將輸入的多行轉換成一行,然後在AppleScript進行反轉。的確是有些trick -_-!——不過後來查資料發現,也有人這麼整。
perl代碼:
@input = ();
while (<>) {
$_=~s/(^\s*)|(\s*$)//g;
push(@input,$_);
}
print join "|" ,@input;
AppleScript代碼:
on run input
set myArray to my theSplit((item 1 of input) as string, "|")
set outputPath to item 1 of myArray
set pagenum to item 2 of myArray as number
set myurl to item 3 of myArray
set tempDir to item 4 of myArray
return myArray
end run
on theSplit(theString, theDelimiter)
-- save delimiters to restore old settings
set oldDelimiters to AppleScript's text item delimiters
-- set delimiters to delimiter to be used
set AppleScript's text item delimiters to theDelimiter
-- create the array
--set theArray to every text item of theString
set theArray to text items of theString
-- restore the old setting
set AppleScript's text item delimiters to oldDelimiters
-- return the result
return theArray
end theSplit
腳本結果,設置到“變量”中
腳本中的變量,是無法設置到腳本中,怎麼辦呢?其也只能通過腳本輸出結果,然後“設定變量的值”控件,對變量進行設置。
- - 設置一個變量
注意:這裏有一個問題,如果腳本輸出的是多行,其實只把第一行字符串,賦值到“變量”中
- 設置多個變量
但是如果要設置多個變量值,咋辦?
於是又是一個trick方法來了,利用上面“只會把第一行賦值給變量”的機制,通過“漏斗”方式逐個對變量進行賦值:
如上圖,依次對“導出路徑”、“頁數”等多個變量進行逐個複製。
其中perl代碼作用是:過濾第一行字符串:
$count=0;
while(<>){
print $_ if $count >0 ;
$count = $count + 1 ;
}
鍵盤錄製&鼠標錄製
Automator提供“錄製”功能,位於右上角:
注意如果你使用錄製功能,需要mac再偏好設置裏,設置一下“允許Automator控制你的電腦”,這樣在運行時纔有用:
但是實際操作起來,我碰到了下面兩個問題:
1、鼠標錄製,總是不準
2、鍵盤錄製,則永遠也沒有錄製上去
第一個問題,我沒有找到很好辦法;最後沒有使用;
第二個問題,不是錄入,而是寫代碼,通過appleScript來實現:
鍵盤操作
腳本代碼是:
on run {input, parameters}
--
set timeoutSeconds to 0
set uiScript to "keystroke \"a\" " --標註,注意這行代碼
my doWithTimeout(uiScript, timeoutSeconds)
return input
end run
on doWithTimeout(uiScript, timeoutSeconds)
set endDate to (current date) + timeoutSeconds
repeat
try
run script "tell application \"System Events\"
" & uiScript & "
end tell"
exit repeat
on error errorMessage
if ((current date) > endDate) then
error "Can not " & uiScript
end if
end try
end repeat
end doWithTimeout
接下來,看看鍵盤操作(均是修改,標註的那一行)幾種情況解決方案:
普通字符
普通字符,比較簡單,使用keystroke ,輸入字符就可以,比如輸入”a”字符:
set uiScript to "keystroke \"a\" "
特殊字符
對於一些特殊字符,沒有特殊ASCII碼,比如:上、下、左、右鍵。因此可以考慮另外一個通過“鍵值”(即操作系統對按鍵的虛擬值)來實現,key code :
比如:“右”按鍵的代碼是:
set uiScript to "key code 124 "
當然,key code 也可以輸入普通字符,如:
“a” 按鍵代碼是:
set uiScript to "key code 0 "
“A”按鍵代碼是:
set uiScript to "key code 0 using {shift down} "
mac系統的虛擬鍵值,可以通過終端命令執行下面命令查詢到:
$ grep '^ *kVK' /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h|tr -d ,|while read x y z;do printf '%d %s %s\n' $z $z ${x#kVK_};done|sort -n
或者你直接查下“附1-key code 表”也行:
組合鍵
上兩個方法,都可以配合組合鍵進行使用:
set uiScript to "key code 124 using {control down, option down, command down,shift down}" --control+option+command+right+shift
循環
Automator對於系列處理動作的流程,製作非常簡單,僅僅需要拖拽基本就可以實現。
但是在分支、循環等這種邏輯上,處理起來要複雜很多。 基本稍微複雜一點的都需要腳本配合纔可以的。
簡單循環
Automator只提供了簡單的“Loop”循環控件,這個循環控件可控執行很差,因爲:
只能從腳本開頭循環
循環策略支持:
A)次數/時間,但是無法通過變量控制
B)詢問方式
試想一下,你就是需要從中間塊循環,但是他非要從開頭開始,你是無法選擇的,這是個多麼想哭的事情。
複雜循環
那麼對於複雜的循環怎麼處理?
Automator支持workflow的調用,因此如果你需要循環的話一種解決方案就是:將需要循環塊的部分,寫成workflow1,然後再主workflow通過腳本控制workflow1的循環。
注意:新建workflow1時,選擇“工組流程”:
這裏實現主要有兩個問題:
1、workflow之間的變量是無法共享的(至少現在我發現是這樣的)。它也只能想字符串一樣傳遞。
但是這個問題:上面腳本使用一節中的“多變量的賦值”方法可以解決。
2、如何腳本調用,workflow?
通過 do shell script cmd,進行執行cmd命令調用,但是這裏有一個問題就是:命令執行是不等待結束立即返回的(實現方法是新起進程執行)——目前還沒有找到能夠等待返回的方法。
on run input
set myArray to my theSplit((item 1 of input) as string, "|")
set outputPath to item 1 of myArray
set pagenum to item 2 of myArray as number
set myurl to item 3 of myArray
set tempDir to item 4 of myArray
delay 10
set mycount to 1000 as number
repeat
run_workflow(tempDir & "/" & (mycount) & ".pdf", "/Users/xxxx/Desktop/test2/自動截頻.workflow")
if pagenum ≤ 1 then
exit repeat
end if
set pagenum to pagenum - 1
set mycount to mycount + 1
--按下右鍵
set timeoutSeconds to 2
set uiScript to "keystroke (ASCII character 29) "
my doWithTimeout(uiScript, timeoutSeconds)
end repeat
delay 1
return myArray
end run
--執行workflow
on run_workflow(inputVars, workflowPath)
set {tid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, linefeed}
set theInputsList to inputVars as text
set AppleScript's text item delimiters to tid
set cmd to "automator -i '" & theInputsList & "' " & workflowPath
return do shell script cmd
end run_workflow
on theSplit(theString, theDelimiter)
-- save delimiters to restore old settings
set oldDelimiters to AppleScript's text item delimiters
-- set delimiters to delimiter to be used
set AppleScript's text item delimiters to theDelimiter
-- create the array
--set theArray to every text item of theString
set theArray to text items of theString
-- restore the old setting
set AppleScript's text item delimiters to oldDelimiters
-- return the result
return theArray
end theSplit
on doWithTimeout(uiScript, timeoutSeconds)
set endDate to (current date) + timeoutSeconds
repeat
try
run script "tell application \"System Events\"
" & uiScript & "
end tell"
exit repeat
on error errorMessage
if ((current date) > endDate) then
error "Can not " & uiScript
end if
end try
end repeat
end doWithTimeout
附
附1-key code 表
十進制 | 十六進制 | 按鍵 |
---|---|---|
0 | 0x00 | ANSI_A |
1 | 0x01 | ANSI_S |
2 | 0x02 | ANSI_D |
3 | 0x03 | ANSI_F |
4 | 0x04 | ANSI_H |
5 | 0x05 | ANSI_G |
6 | 0x06 | ANSI_Z |
7 | 0x07 | ANSI_X |
8 | 0x08 | ANSI_C |
9 | 0x09 | ANSI_V |
10 | 0x0A | ISO_Section |
11 | 0x0B | ANSI_B |
12 | 0x0C | ANSI_Q |
13 | 0x0D | ANSI_W |
14 | 0x0E | ANSI_E |
15 | 0x0F | ANSI_R |
16 | 0x10 | ANSI_Y |
17 | 0x11 | ANSI_T |
18 | 0x12 | ANSI_1 |
19 | 0x13 | ANSI_2 |
20 | 0x14 | ANSI_3 |
21 | 0x15 | ANSI_4 |
22 | 0x16 | ANSI_6 |
23 | 0x17 | ANSI_5 |
24 | 0x18 | ANSI_Equal |
25 | 0x19 | ANSI_9 |
26 | 0x1A | ANSI_7 |
27 | 0x1B | ANSI_Minus |
28 | 0x1C | ANSI_8 |
29 | 0x1D | ANSI_0 |
30 | 0x1E | ANSI_RightBracket |
31 | 0x1F | ANSI_O |
32 | 0x20 | ANSI_U |
33 | 0x21 | ANSI_LeftBracket |
34 | 0x22 | ANSI_I |
35 | 0x23 | ANSI_P |
36 | 0x24 | Return |
37 | 0x25 | ANSI_L |
38 | 0x26 | ANSI_J |
39 | 0x27 | ANSI_Quote |
40 | 0x28 | ANSI_K |
41 | 0x29 | ANSI_Semicolon |
42 | 0x2A | ANSI_Backslash |
43 | 0x2B | ANSI_Comma |
44 | 0x2C | ANSI_Slash |
45 | 0x2D | ANSI_N |
46 | 0x2E | ANSI_M |
47 | 0x2F | ANSI_Period |
48 | 0x30 | Tab |
49 | 0x31 | Space |
50 | 0x32 | ANSI_Grave |
51 | 0x33 | Delete |
53 | 0x35 | Escape |
55 | 0x37 | Command |
56 | 0x38 | Shift |
57 | 0x39 | CapsLock |
58 | 0x3A | Option |
59 | 0x3B | Control |
60 | 0x3C | RightShift |
61 | 0x3D | RightOption |
62 | 0x3E | RightControl |
63 | 0x3F | Function |
64 | 0x40 | F17 |
65 | 0x41 | ANSI_KeypadDecimal |
67 | 0x43 | ANSI_KeypadMultiply |
69 | 0x45 | ANSI_KeypadPlus |
71 | 0x47 | ANSI_KeypadClear |
72 | 0x48 | VolumeUp |
73 | 0x49 | VolumeDown |
74 | 0x4A | Mute |
75 | 0x4B | ANSI_KeypadDivide |
76 | 0x4C | ANSI_KeypadEnter |
78 | 0x4E | ANSI_KeypadMinus |
79 | 0x4F | F18 |
80 | 0x50 | F19 |
81 | 0x51 | ANSI_KeypadEquals |
82 | 0x52 | ANSI_Keypad0 |
83 | 0x53 | ANSI_Keypad1 |
84 | 0x54 | ANSI_Keypad2 |
85 | 0x55 | ANSI_Keypad3 |
86 | 0x56 | ANSI_Keypad4 |
87 | 0x57 | ANSI_Keypad5 |
88 | 0x58 | ANSI_Keypad6 |
89 | 0x59 | ANSI_Keypad7 |
90 | 0x5A | F20 |
91 | 0x5B | ANSI_Keypad8 |
92 | 0x5C | ANSI_Keypad9 |
93 | 0x5D | JIS_Yen |
94 | 0x5E | JIS_Underscore |
95 | 0x5F | JIS_KeypadComma |
96 | 0x60 | F5 |
97 | 0x61 | F6 |
98 | 0x62 | F7 |
99 | 0x63 | F3 |
100 | 0x64 | F8 |
101 | 0x65 | F9 |
102 | 0x66 | JIS_Eisu |
103 | 0x67 | F11 |
104 | 0x68 | JIS_Kana |
105 | 0x69 | F13 |
106 | 0x6A | F16 |
107 | 0x6B | F14 |
109 | 0x6D | F10 |
111 | 0x6F | F12 |
113 | 0x71 | F15 |
114 | 0x72 | Help |
115 | 0x73 | Home |
116 | 0x74 | PageUp |
117 | 0x75 | ForwardDelete |
118 | 0x76 | F4 |
119 | 0x77 | End |
120 | 0x78 | F2 |
121 | 0x79 | PageDown |
122 | 0x7A | F1 |
123 | 0x7B | LeftArrow |
124 | 0x7C | RightArrow |
125 | 0x7D | DownArrow |
126 | 0x7E | UpArrow |