Extract Safari reading list and bookmark URLs with Node.js

While organising files in preparation for moving to a shiny new Mac, I wanted to compile a list of all the Safari reading list entires I'd made over the years. The data for this is held in a .plist file - Apple's JSON like text/binary format - and is fairly easy to extract.

To do so, npm init, then install a plist parser. This translates the plist data into a nice JSON object. I used bplist-parser, which can be installed with npm i bplist-parser. Then create an index.js file and fill it with the following code:

1 const fs = require("fs");
2 const bplist = require("bplist-parser")(async () => {
3 const obj = await bplist.parseFile(
4 "/Users/YOUR_NAME/Library/Safari/Bookmarks.plist"
5 );
6
7 const buildList = (rawList) => {
8 const resArr = [];
9 rawList[0].Children.map((x) => {
10 const resStr = `${x.URIDictionary.title} - ${x.URLString}`;
11 resArr.push(resStr);
12 });
13 return resArr.join("\n");
14 };
15
16 const bookmarksBar = obj[0].Children.filter((x) => x.Title === "BookmarksBar");
17 const bookmarksMenu = obj[0].Children.filter((x) => x.Title === "BookmarksMenu");
18 const readingList = obj[0].Children.filter(
19 (x) => x.Title === "com.apple.ReadingList"
20 );
21
22 // change the argument to one of the three above to change the output
23 const list = await buildList(readingList);
24
25 fs.writeFileSync("./reading_list", list);
26 })();
27

Make sure to allow your terminal file access (in Settings => Security & Privacy => Privacy), insert your profile name where the 'YOUR NAME' placeholder is, then run node index.js and your list will be output.