Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

AppleScript: Safari - Save As Web Archive

The title says it all: Can someone tell me please how - most likely via UI scripting, since I can't see anything in the dictionary - I can get Safari to save the front most document as a Web Archive using AppleScript?

Posted on Sep 24, 2011 3:47 AM

Reply
Question marked as Best reply

Posted on Sep 24, 2011 5:25 AM

Hi Bernard,


The following script should do the trick:


tell application "Safari" to activate


tell application "System Events"

tell process "Safari"

keystroke "s" usingcommand down-- Save As…

repeat until sheet 1 of window 1 exists

delay 0.1

end repeat

keystroke "d" usingcommand down-- save to Desktop

tell sheet 1 of window 1

clickpop up button 1 of group 1

keystroke "W" & return-- Web Archive

keystrokereturn-- Save

if sheet 1 exists then -- that is "sheet 1 of sheet 1"

click button "Replace" of sheet 1

end if

end tell

end tell

end tell

18 replies
Question marked as Best reply

Sep 24, 2011 5:25 AM in response to Arkouda

Hi Bernard,


The following script should do the trick:


tell application "Safari" to activate


tell application "System Events"

tell process "Safari"

keystroke "s" usingcommand down-- Save As…

repeat until sheet 1 of window 1 exists

delay 0.1

end repeat

keystroke "d" usingcommand down-- save to Desktop

tell sheet 1 of window 1

clickpop up button 1 of group 1

keystroke "W" & return-- Web Archive

keystrokereturn-- Save

if sheet 1 exists then -- that is "sheet 1 of sheet 1"

click button "Replace" of sheet 1

end if

end tell

end tell

end tell

Sep 24, 2011 11:37 AM in response to Pierre L.

I have hit a problem:


When I put your suggested script into a loop where the URL of the frontmost document is set by the script, I get stuck in the loop after the keystroke "s" that's waiting for sheet 1 of window 1 to exist.


I thought at first it was because tell "System Events" etc was nested in an overall tell "Safari", but even de-nesting makes no difference. It's as though the cmd-S to bring up the save dialogue is not happening. (If I do cmd-S from the keyboard whilst in the loop, the script will move on.)


Any idea what's happening? (The following is a mix of AS and pseudocode just to show structure.)



tell app "Safari" to activate


repeat with theURL in a list


tell app "Safari"

set the URL to theURL

wait until loaded

end tell


tell app "System Events"

tell process "Safari"

keystroke "s" using command down

repeat until sheet 1 of window 1 exists

delay 1

end repeat

end tell

end tell


...


end repeat

Sep 24, 2011 12:35 PM in response to Arkouda

I don't know how you check that the page is fully loaded. The following method seems to work pretty well:


set theList to {"http://www.apple.com/", "http://www.macrumors.com/", "http://hints.macworld.com/", "http://www.ee.surrey.ac.uk/Teaching/Unix/"}


tell application "Safari"

activate

repeat with theURL in theList

set the URL of front document to theURL

tell application "System Events"

tell process "Safari"

tell menu 1 of menu bar item "File" of menu bar 1

repeat while enabled of menu item "Save as…" is false

delay 0.1

end repeat

end tell

keystroke "s" usingcommand down-- Save As…

repeat until sheet 1 of window 1 exists

delay 0.1

end repeat

keystroke "d" usingcommand down-- save to Desktop

tell sheet 1 of window 1

clickpop up button 1 of group 1

keystroke "W" & return-- Web Archive

keystrokereturn-- Save

if sheet 1 exists then -- that is "sheet 1 of sheet 1"

click button "Replace" of sheet 1

end if

end tell

end tell

end tell

end repeat

end tell


Message was edited by: Pierre L. (I've replaced “Fichier” with “File”.)

Sep 24, 2011 2:02 PM in response to Pierre L.

to test if a page is fully loaded, test in javascript:


set targetURL to "http://www.yahoo.com"

tell application "Safari"

tell document 1

set URL to targetURL


delay 0.5 -- so it doean't catch that last loaded page; probably more elegant ways of doing this

repeat while ((do JavaScript "document.readyState") ≠ "complete")

delay 0.1

end repeat

end tell

say "done"

end tell

Sep 24, 2011 3:40 PM in response to twtwtw

Thanks, twtwtw, for that information.


Maybe I wasn't clear in my previous post, but when I said “I don't know how you check that the page is fully loaded”, I was actually meaning “You did not explained to me how you managed to check that the page was fully loaded” and not “I don't know how to check that the page is fully loaded.”


Between


repeat while enabled of menu item "Save as…" is false

delay 0.1

end repeat


and


repeat while ((do JavaScript "document.readyState") ≠ "complete")

delay 0.1

end repeat


I think I prefer the former one (that is, pure AppleScript), unless it proves to be less efficient.


Message was edited by: Pierre L.

Sep 24, 2011 4:11 PM in response to Pierre L.

I was not able to answer your question, Pierre, before twtwtw's response but my test was the JavaScript method. Not generally being a UI scripting proponent, I had not thought of your method, but I see your point about it being 'pure AppleScript'.


I suspect that the reason I need to inject the delay with the JavaScript method is that it confirms completion before the UI has had a chance to update - therefore, preventing cmd-S from being 'seen' by Safari. In most cases that would not matter, but if one is then relying on UI scripting it does.

Mar 8, 2012 9:01 AM in response to Alex Zavatone

Thanks for the information.


I've searched the Web and found a few discussions about that issue, like this one.

Then, in order to test the delay command on my own, I've saved the following script as an application:


beep 1

repeat 300 times

delay 0.1

end repeat

beep 2


Here's a screenshot of the Activiy Monitor window while running the above script:


User uploaded file


Message was edited by: Pierre L.

Mar 8, 2012 9:40 AM in response to Pierre L.

What ended up surprising me Pierre, was that first sleep worked when run as an AS script. But when run in an applet, it used 100% of a core. Then Apple fixed that. Then I moved my scripts into Xcode and guess what. When run as a compiled app, it again used 100% of a core.


So, to avoid wasting more time, I'll just use shell sleep like so:


do shell script "sleep 2"


Sorry I didn't include that information before.

Dec 4, 2012 5:49 AM in response to Arkouda

This is one I have recently written that saves all tabs in a window, it remembers the parent folder until next time, and also uses the webarchive ending when Safari is in doubt. You can set properties for whether you like it to overwrite or not. Please look at Macscripter for any updates.



property tlvl : me

# Release 1.0.1

# © 2012 McUsr and put in Public Domain under GPL 1.0

# Please refer to this post: http://macscripter.net/post.php?tid=30892

property shallClose : false # set this to false if you don't want to close the windows, just saving them

property dontOverWriteSavedTabs : false # set this to true if you don't want to overwrite already saved tabs in the folder

script saveTabsInSafariWindowsToFolder

property parent : AppleScript


property scripttitle : "SafariSaveTabs"

on run

if downloadWindowInFront() then return 0 # activates Safari


local script_cache

set script_cache to my storage's scriptCache()


set saveFolder to POSIX path of (getHFSFolder({theMessage:"Choose or create folder to save Safari-tabs in.", hfsPath:DefaultLocation of script_cache as alias}))

if saveFolder = false then return 0 -- we were obviously mistaken, about what we wanted to do.


my storage's saveParenFolderInScriptCache(saveFolder, script_cache)


tell application "Safari"

tell its window 1

local tabc, oldidx

set tabc to count tabs of it

if not tlvl's shallClose then

set oldidx to index of current tab

tell tab tabc to do JavaScript "self.focus()"

end if

local saveCounter

set saveCounter to 1 -- regulates setting of save folder to only first time in Safari.

repeat while tabc > 0

local theUrl, theIdx, theProtocol, alreadyClosed


set {theUrl, theIdx, alreadyClosed} to {URL of its current tab, index of its current tab, false}


if my isntAduplicateTab(theIdx, it) then


set theProtocol to my urlprotocol(theUrl)

if theProtocol is in {"http", "https"} then

# save it

set saveCounter to my saveCurrentTab(saveFolder, saveCounter)

else if theProtocol is "file" then

# make an alias of it

my makeAliasForAFurl(saveFolder, theUrl)

end if

else

if tlvl's shallClose then

close current tab

set alreadyClosed to true

end if

end if


if not alreadyClosed and tlvl's shallClose then

close current tab of it

set tabc to tabc - 1

else if not tlvl's shallClose then

set tabc to tabc - 1

if tabc > 0 then tell tab tabc to do JavaScript "self.focus()"

end if

end repeat

# move forwards

if not tlvl's shallClose then

tell tab oldidx to do JavaScript "self.focus()"

end if

end tell

end tell

end run



to makeAliasForAFurl(destinationFolder, furl)

local ti, tids, thefilePath

set ti to "file://"

set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ti}

set thefilePath to text item 2 of furl

set AppleScript's text item delimiters to tids

set theFile to POSIX file thefilePath as alias

set theFolder to POSIX file destinationFolder

tell application "Finder"

make alias at theFolder to theFile

# I don't care if there was one there from before, as it could equally

# be a file with the same name.

end tell

end makeAliasForAFurl


to saveCurrentTab(destinationFolder, timeNumber)

tell application id "sfri" to activate

tell application "System Events"

set UI elements enabled to true

tell process "Safari"

keystroke "s" using {command down}

tell window 1

repeat until exists sheet 1

delay 0.2

end repeat

tell sheet 1

if timeNumber = 1 then -- We'll set the savepath upon first call

keystroke "g" using {command down, shift down}

repeat until exists sheet 1

delay 0.2

end repeat

tell sheet 1

set value of text field 1 to destinationFolder

click button 1

delay 0.1

end tell

end if

keystroke return

delay 0.2

if exists sheet 1 then -- We are being asked if we want to overwrite already saved tab

if dontOverWriteSavedTabs then

keystroke return # if it was already saved. We don't overwrite it

click button 3

else

keystroke tab

keystroke space # we are to overwrite

end if

else

try

set dummy to focused of sheet 1

on error

# click button 1 of panel of application "Safari"

keystroke return


delay 0.2

if exists sheet 1 then -- We are being asked if we want to overwrite already saved tab

if dontOverWriteSavedTabs then

keystroke return # if it was already saved. We don't overwrite it

click button 3

else

keystroke tab

keystroke space # we are to overwrite

end if

end if

end try

end if

end tell

end tell

end tell

end tell

set timeNumber to timeNumber + 1

return timeNumber

end saveCurrentTab


on downloadWindowInFront()

tell application "Safari"

activate

set tabCount to count tabs of its window 1

if tabCount < 1 then

tell application "SystemUIServer" to activate

activate

return true # Downloads window or somethingelse

end if

end tell

return false

end downloadWindowInFront


on isntAduplicateTab(idxOfCurrentTab, theWin)

using terms from application "Safari"

tell theWin

set curTabname to name of tab idxOfCurrentTab

set curTabUrl to URL of tab idxOfCurrentTab

repeat with i from (idxOfCurrentTab - 1) to 1 by -1

if name of tab i = curTabname and URL of tab i = curTabUrl then return false

end repeat

return true

end tell

end using terms from

end isntAduplicateTab


on getHFSFolder(R) -- Returns hfsPathAsText

-- R : {Amessage:theMessage,hfsPath:aStartPath}

local new_path, failed

set failed to false

tell application "SystemUIServer"

activate

repeat while true

try

set new_path to (choose folder with prompt (theMessage of R) default location (hfsPath of R) without invisibles) as text

on error e number n

if n is -128 then

set failed to true

exit repeat

end if

end try

exit repeat

end repeat

end tell

if failed is true then

return false

else

return new_path

end if

end getHFSFolder


on urlprotocol(anUrl)

# returns the protocol of an Url, i.e. http, https, file, localhost etc.

local tids, theProtocol

set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "://"}

set theProtocol to text item 1 of anUrl

set AppleScript's text item delimiters to tids

return theProtocol

end urlprotocol


to parentfolder for aPath

local colons, slashes, origDelims

set {colons, slashes} to {false, false}


if (offset of ":" in aPath) > 0 then set colons to true

if (offset of "/" in aPath) > 0 then set slashes to true


if colons and slashes then

return null

else if colons then

set origDelims to ":"

else if slashes then

set origDelims to "/"

else

return null

end if

local tids

set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, origDelims}

if aPath = "/" then

-- we return root when we get root

set AppleScript's text item delimiters to tids

return "/"

end if

local theParentFolder

if text -1 of aPath is in {":", "/"} then

set theParentFolder to text items 1 thru -2 of text 1 thru -2 of aPath

else

set theParentFolder to text items 1 thru -2 of aPath

end if

set theParentFolder to theParentFolder as text

if slashes and theParentFolder = "" then set theParentFolder to "/"

-- sets the root path if we got a folder one level below it

if colons and (":" is not in theParentFolder) then set theParentFolder to theParentFolder & ":"

-- we return volumename, if we are given volumename

set AppleScript's text item delimiters to tids

return theParentFolder

end parentfolder



script storage

property cachespath : ((path to library folder from user domain as text) & "caches:" & "net.mcusr." & scripttitle)


on scriptCache()


local script_cache

try

set script_cache to load script alias (my cachespath)

on error

script newScriptCache

property DefaultLocation : (path to desktop folder as text)

# edit any of those with default values

end script


set script_cache to newScriptCache

end try

return script_cache

end scriptCache


to saveScriptCache(theCache)

store script theCache in my cachespath replacing yes

end saveScriptCache


to saveParenFolderInScriptCache(theFolderToSaveIn, script_cache)

local containingFolder

set containingFolder to (parentfolder of saveTabsInSafariWindowsToFolder for theFolderToSaveIn) & "/"

local theLoc

set theLoc to POSIX file containingFolder as alias

set DefaultLocation of script_cache to theLoc

my saveScriptCache(script_cache)

end saveParenFolderInScriptCache

end script

end script

tell saveTabsInSafariWindowsToFolder to run

AppleScript: Safari - Save As Web Archive

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.