Tab is not a reliable method of navigating links in Safari on my 10.9.5 system.
This macro will scrape the links out of a Google search page and send them to TextWrangler (although you can change TextWrangler to BBEdit in the script to use BBEdit instead).
It produces a list with the link titles and links formatted in a table like so:
Thanks Chris, you are always a rock star in your approach. I also noticed that with the tab it would often do every other link, which seems very bizarre.
I look forward to seeing your following AppleScript.
I like to position my window in TextWrangler (BBEdit for me) so I can see the edge of it behind Safari. That lets me see the line numbers in TextWrangler to let me know how many more URLs I have to process.
On my system I have plugins for more sites than Google. I use a folder in the Finder to contain scripts named with the base-domain of the site to be searched. So it's relatively easy to add a new site to my link scraper.
Thanks – a useful approach and good food for thought.
For reference, in case it's of any interest, here's one way of opening all the tabs from the shell (after gathering them with JXA (and in-browser) Javascript), and passing them on in Markdown format.
So I had been using this almost daily until I upgraded my Mac and it broke any ideas?
Keyboard Maestro 8.2.2 “Harvest Google result links, opening each in own tab” Macro
And here is an updated JXA script, which seems to be working on Sierra, with KM 8.2.2 and Safari 11.1.2 (Develop > Allow JavaScript from Apple Events enabled)
(() => {
'use strict';
const main = () => {
const
strXPath = "//*[@class='r']/a",
saf = Application("Safari"),
ws = saf.windows,
lrHarvest = bindLR(
0 < ws.length ? (
Right(ws.at(0))
) : Left('No window open in Safari'),
w => {
const
xs = pageXPathHarvest(
saf, w, strXPath
);
return 0 < xs.length ? (
// Safari effect
tabsOpened(saf, w, xs),
// Keyboard Maestro value
Right(
xs.reduce(
(a, link) =>
`${a}[${link[0]}](${link[1]})\n`,
''
)
)
) : Left(
'Perhaps not a Google search page ?\n' +
'(No links matching "' + strXPath + '")'
);
}
);
return lrHarvest.Left || lrHarvest.Right;
};
// SAFARI ---------------------------------------------
// Harvest elements from Safari by XPath pattern
const pageXPathHarvest = (browser, oWin, strXPath) =>
browser.doJavaScript(
`(${xpathHarvest})("${strXPath}")`, { in
: oWin.currentTab
}
);
// tabsOpened :: Application -> Window -> (String, String) -> IO()
const tabsOpened = (safari, oWin, links) => {
const winTabs = oWin.tabs;
links.map(link => winTabs.push(safari.Tab({
url: link[1]
})))
}
// Harvesting function to run in the browser context
const xpathHarvest = strPath => {
const
r = document.evaluate(strPath, document, null, 0, null),
xs = [];
var oNode;
while (oNode = r.iterateNext()) {
xs.push([oNode.text, oNode.href]);
}
return xs;
};
// GENERIC FUNCTIONS ----------------------------------
// https://github.com/RobTrew/prelude-jxa
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
const bindLR = (m, mf) =>
m.Right !== undefined ? (
mf(m.Right)
) : m;
// MAIN
return main();
})();
I did activate: Develop > Allow JavaScript from Apple Events enabled
Perhaps I am missing something and this is not what the script does. It's just the AppleScript I was using made by @ccstone is failing in Sierra and above now.
Here is the error I get when I run it:
/var/folders/5l/5s7qw5ld1xxglm0094_hyh_c0000gn/T/Keyboard-Maestro-Script-81ECA7A6-72E3-4B9E-B603-D65552A765FE:5:6: script error: Expected expression but found “>”. (-2741)
This one works in that it opens all the links in new tabs. I just wanted a script that will open first link only in current tab.
And a way to specify which link to open in the script so I can have 4 different hotkeys for opening either 1st or 2nd or 3rd or 4th link with a macro for each one.
Got it – so what you need, in place of the tabsOpened function, is something like:
// nthLinkOpened :: Application -> (String, String) -> Int -> IO()
const nthLinkOpened = (safari, links, i) => {
const
ds = safari.documents,
d = ( // Ensuring that there is an open document
ds.length < 1 && ds.push(safari.Document()),
ds.at(0)
);
// i is a zero-based index into the link list.
return d.url = links[i][1];
};
For some reason none of these are working for me anymore. I am on Safari 14 with "Allow JavaScript from Apple Events" checked in the Develop tab. Any idea? It use to open in several tabs and then for a while it would go on endlessly and I would have to stop the script from running and now it just doesn't work.