@Matts 给出了关于在 Automator 中使用变量的一个很好的答案。但是,在更深入地讨论了您的目标的性质之后,很明显知道如何在 Automator 中引用变量根本不是问题,也不会帮助您。
事实上,甚至不用Automator。它不会做你想做的事。尝试与 Safari 进行物理交互自动化的整个努力意味着它将涉及相当多的 AppleScripting,而且它是最糟糕的一种——GUI(图形用户界面)脚本。
从Automator中出来并使用Script Editor。确保 脚本编辑器 具有相关的可访问权限,以便 GUI 脚本工作。您可以通过转到系统偏好设置>安全和隐私>隐私>辅助功能,解锁窗口左下方的挂锁,然后在脚本编辑器中找到允许控制您的计算机的应用程序列表。确保已选中。
简而言之,GUI 脚本几乎就是这样做的:控制您的计算机,使其能够发出鼠标点击并按下按钮或菜单项,就好像用户在现场亲自操作一样。
但是,弄清楚如何在可按下或单击的屏幕元素的 GUI 层次结构中引用对象是一场噩梦。
我认为您的 Automator 制造代码的 sn-p 误导了您:
click UI Element "2017-01-03” of group 7 of UI Element 1 of scroll area 1 of ¬
group 23 of UI Element 1 of scroll area 1 of group 1 of group 1 of tab group 1 ¬
of splitter group 1 of window "..." of application process "Safari"
我想您看到了UI Element "2017-01-03" 并推断必须能够通过用适当的日期字符串替换当前存在的日期字符串来访问特定日期的数据。因此,您为什么要在 Automator 中使用变量。但它不会奏效。该日期字符串仅报告元素的名称(恰好是称为static text 元素的东西),其中包含一段带有日期的文本,但不是可编辑的。因此,引用UI Element "2017-01-02" 只会抛出错误,因为不存在这样的元素。
这就是我目前所想出的。这绝不是一个完整的包:我还没有编写可以直接实现您的目标的脚本,但我肯定会为您提供所有必要的组件,以便您能够拼凑出一个成品。相信我,我做了其中最累人的部分,这是一个艰苦的搜索,以获取您希望能够在 GUI 层次结构中单击的每个按钮的所有特定对象引用。
首先,重要说明:我没有使用您在 cmets 中发布的 URL 来进行我的 GUI 检查。该网页过于繁忙和复杂,这会使工作变得更加困难。但幸运的是,他们有一个指向页面的链接,该页面仅包含我们将与之交互的小部件。但是,由于页面的 GUI 构成不同(小部件与您的原始链接相同,只是页面的其余部分不同),我编写的任何代码都不适用于您的原始链接。所以你必须使用我在脚本中提供的 URL,即https://www.dukascopy.com/plugins/fxMarketWatch/detach.php?id=ct23。
引用 GUI 对象
声明一些属性变量,您可以根据要从中检索数据集的月份和年份进行更改。您还可以通过简短的缩写代码或其完整描述来指定您的仪器:
property |URL| : "https://www.dukascopy.com/plugins/fxMarketWatch/detach.php?id=ct23"
property Instrument : {|short|:"AAPL.US/USD", |description|:"APPLE INC"}
property |month| : "December"
property |year| : "2017"
下一部分是 System Events 和 Safari 进程使用的地方,它是 GUI 脚本命令的根。我还将它与我们将要与之交互的所有元素的所有元素混为一谈。否则,您必须一遍又一遍地写出它们,而将它们作为一组 UI 元素引用一次会更容易,这些元素构成 tell... 块的基础:
tell application "System Events" to ¬
tell application process "Safari" to ¬
tell (window named |URL|) to ¬
tell UI element 1 of scroll area 1 of group 1 of ¬
UI element 1 of scroll area 1 of group 1 of ¬
group 1 of tab group 1 of splitter group 1
.
.
(* The rest of our code goes
inside this tell block *)
.
.
end tell
另请注意,命令将发送到与网页 URL 命名相同的窗口。所以这个脚本假定网页已经在 Safari 中加载——它不会为你加载它(尽管实现起来并不困难)。
还要确保在加载页面后登录。如果不这样做,单击下载按钮会弹出登录框,这将中断整个脚本并引发错误。但是,一旦您登录并单击框以“保持登录”,从那时起它将自行进入下载选项。
下一部分是努力的成果。这是我定义变量的地方,每个变量都引用一个 GUI 对象,您希望能够从脚本发出点击和按下。
这三个构成了您可以选择的每一行可用工具的主要组件:
set Instruments to a reference to groups of group 1 of group 1 of group 3
--> All Instruments (410 of them)
set Instrument_Abbreviations to a reference to static texts of group 1 of group 2 of Instruments
--> The short text code for each instrument
set Instrument_Descriptions to a reference to static texts of group 2 of Instruments
--> The long text for each instrument
这是用于选择时区、本地或 GMT 的一对元素:
set LOCAL_GMT to a reference to checkboxes of group 1 of group 7 of group 4
下一个元素块需要一些工作才能浏览。这些是负责选择日期的人。按日期按钮可在屏幕上弹出日历,您可以在其中选择年、月和日。这也将构成您的程序循环的核心,因为您使用变量在一个月的几天中循环以检索所有数据文件,例如 12 月。
菜单和对象(例如这个日历)“弹出”的地方在于,它们在创建并出现在我们的屏幕上之前并不实际存在。因此,在 GUI 脚本中,当脚本尝试引用将在未来某个时间点存在但当前不存在且还不存在的对象时,可能会引发错误。使用a reference to 是解决这个问题的一种方法,我已经为大多数变量声明做到了这一点。
set Date_button to a reference to button 1 of group 3 of group 4
--> The date button
set Calendar_year_button to a reference to button 2 of UI element 2 of row 1 of table 1 of group 5
--> The button that pops up the year menu (as shown)
set Calendar_year_list_items to a reference to static texts of groups of group 1 of group 1 of UI element 1 of row 1 of table 1 of group 5
--> The year menu
set Calendar_month_button to a reference to button 2 of UI element 1 of row 1 of table 1 of group 5
--> The button that pops up the month menu
set Calendar_month_list_items to a reference to static texts of groups of group 1 of group 1 of UI element 1 of row 1 of table 1 of group 5
--> The month menu
set Calendar_days to a reference to static texts of UI elements 2 thru -1 of rows 3 thru -2 of table 1 of group 5
--> All the day text objects visible at any one time
--> 6 rows x 7 days = 42 text objects
接下来的三个变量用于单击“勾选”按钮,该按钮会弹出一个菜单。这组中的最后一对是 BID/ASK 按钮。
set Tick_button to a reference to static text 1 of group 1 of group 1 of list 2 of group 1 of group 4
--> The tick button
set Tick_menu_items to a reference to menu items of list 1
--> The pop-up menu items as shown below
set BID_ASK to a reference to checkboxes of group 1 of group 2 of group 4
--> The "BID" and "ASK" buttons
接下来是下载按钮:
set Download_button to a reference to button "Download" of group 8 of group 4
现在,当单击下载按钮时,GUI 会在此底部进行重建。首先,会显示一个进度条来代替所有暂时消失的按钮:
我忽略了这些元素,因为我认为没有必要在这么短的时间内访问取消按钮。
然后另一个重建和“另存为 CVS”和“重置”按钮出现。我们都需要:
set Reset_button to a reference to button "Reset" of group 2 of group 4
set Save_button to a reference to button "Save as .csv" of group 2 of group 2 of group 4
现在我们拥有了我们需要访问的所有 GUI 对象,通过引用存储在一组变量中,我们(我的意思是你)可以利用这些变量来执行实际的自动化过程。
执行一些操作
一些变量包含对可以直接执行操作的元素的引用,例如click Date_button。但是,其他人会抛出错误或无法按照您的预期执行。这是因为一些变量包含对单个对象的引用,例如Date_button,而其他变量包含对嵌套列表中的几个不同对象或对象组的引用,例如Calendar_year_list_items。
在使用这些嵌套列表之前,我们必须将它们展平为一个列表。以下函数将执行此操作,并且可以添加到脚本的最后(end tell:
on flattened(L) -- recurse nested lists and return a one-dimensional list
if class of L is not list then
return [L]
else if length of L is 0 then
return L
else
return flattened(the first item of L) & ¬
flattened(the rest of L)
end if
end flattened
现在我们拥有了开始让这些对象执行某些操作所需的一切。为此,我认为最好编写另一个函数。它可以在on flattened 函数声明之前:
on Download_CSV_Data for _instrument from _date
local _instrument, _date
set [_day, _month, _year] to [day, month, year] of date _date
global Instruments, Instrument_Abbreviations, Instrument_Descriptions, LOCAL_GMT, ¬
Date_button, Calendar_year_button, Calendar_year_list_items, Calendar_month_button, ¬
Calendar_month_list_items, Calendar_days, Tick_button, Tick_menu_items, ¬
BID_ASK, Download_button, Reset_button, Save_button
tell application "System Events"
click 1st item of my flattened(Instrument_Abbreviations whose name is _instrument)
-- click 1st item of my flattened(Instrument_Descriptions whose name is Instrument's |description|)
click Tick_button
click (Tick_menu_items whose name is "Second")
click (BID_ASK whose name is "ASK")
click (LOCAL_GMT whose name is "GMT")
click Date_button
click Calendar_year_button
click 1st item of my flattened(Calendar_year_list_items whose name is _year)
click Calendar_month_button
click 1st item of my flattened(Calendar_month_list_items whose name is (_month as text))
click 1st item of my flattened(Calendar_days whose name is _day)
click Download_button
repeat while not (exists Reset_button)
delay 1
end repeat
click Save_button
click Reset_button
end tell
end Download_CSV_Data
要下载与特定日期相关的数据文件,您只需调用该函数即可。您可以将该函数调用放在第一个 end tell 语句(变量声明之后)和第一个函数声明之前的广告之间,因此您的脚本将如下所示:
property |URL| : "https://www.dukascopy.com/plugins/fxMarketWatch/detach.php?id=ct23"
property Instrument : {|short|:"AAPL.US/USD", |description|:"APPLE INC"}
property |month| : "December"
property |year| : "2017"
-- GUI object variable declarations
tell application "System Events" to ¬
tell application process "Safari" to ¬
tell (window named |URL|) to ¬
tell UI element 1 of scroll area 1 of group 1 of ¬
UI element 1 of scroll area 1 of group 1 of ¬
group 1 of tab group 1 of splitter group 1
.
.
.
end tell
(*** Insert function calls here ***)
Download_CSV_Data for Instrument's |short| from "17/12/2017"
on Download_CSV_Data for _instrument from _date
.
.
.
end Download_CSV_Data
on flattened(L) -- recurse nested lists and return a one-dimensional list
.
.
.
end flattened
如果您在美国,您可能需要在短时间内调换月份和日期。所以我写了Download_CSV_Data for Instrument's |short| from "17/12/2017",你可能必须写Download_CSV_Data for Instrument's |short| from "12/17/2017"(使用你的计算机系统使用的任何短日期格式)。