Over the last day or so I’ve been trying to find a decent alternative to Yahoo’s Delicious online bookmarking service.  It’s a service I’ve used for many years (since 2006, in fact) and, unfortunately, Yahoo has sold the service to AVOS.  To be fair, AVOS does plan to keep Delicious running and also implement improvements but with the purchase of an iPhone 4 it makes sense for me to transition to a service that I can synchronise between all my devices.

What is the chosen alternative?

I’ve been a long-time user of Evernote and its ability to sync between Mac (all my hardware), Windows (at work) and iPhone (in my pocket) makes it a sensible choice for the transition.  However, I needed to find a way to transfer all 2628 of my bookmarks to Evernote.

First attempt

My first go was to use the web service from Dr. Palaniraja but that didn’t do some of the things I needed.  For example:

  • It didn’t keep my tags, despite the website saying it would (this effectively made it useless for me)
  • It required a lot of manual input.  I don’t mind this so much but less technical users probably would.

I was looking for an automated way to transfer everything.  My chosen method is an AppleScript that I found online at Veritrope, which I then heavily modified to do more of the things I wanted.

My changes are as follows:

  • The original script verified every URL before adding it to Evernote.  Conceptually this is fine but I wanted to offer the option of not doing that.
  • The original script used the bookmark’s URL as the note title.  To me this is a little odd so I modified it to use the bookmark’s actual title as the note’s title, too.
  • The original script required some manual editing to set your location i.e. Japan, U.S. or outside Japan/U.S. I’m in Australia which meant manually changing the location.  This is easy but I wanted to prompt the user for this information.
  • The original script saved the bookmark’s entire page content as the content of the new note.  This is a great idea but I wanted the option of not doing this, if necessary.  The main reason for this is so that Evernote’s basic account holders don’t blow the cap on their monthly usage.
  • The original script, if verifying URLs, saved all the bad URLs to a single note.  I modified mine to save them as individual notes in a special, local-only, notebook called "Bad Bookmark List".

The script

Here’s the entire script.  All you need to do is:

  • Export your bookmarks from Delicious using their export/download page.
  • Copy/paste the script below into AppleScript Editor (Mac only!) and run it.

The only manual change necessary, if required, is to change the name of the destination notebooks for the good and bad bookmarks.  These two settings are evNotebook and badNotebook respectively.

Warning: Be aware that if you run this script over a big bookmark list and select to verify URLs, it may take a long time to run!

-- v1.30
-- http://veritrope.com
-- Delicious Bookmark Importer for Evernote
-- February 18, 2011
-- Status, Latest Updates, and Comments Collected at:
-- http://veritrope.com/code/delicious-bookmark-importer-for-evernote/
--
-- v2.0
-- June 14th, 2011
-- Chris Rasmussen, digitalformula.net
-- Changes:
-- Added prompt for verifying URL
-- Added prompt for saving notes with actual title as title or URL as title
-- Added prompt for location i.e. U.S. or Japan or everywhere else
-- Added prompt for saving the bookmark or full page as the note's content (recommended for Evernote premium users, only)
--
-- Note : The above prompts would usually be too many but since this script is probably intended to be run once only,
-- more options are better than none
--
-- v2.1
-- June 14th, 2011
-- Chris Rasmussen, digitalformula.net
-- Changes from 2.0:
-- Bad bookmarks are now added to a notebook called 'Bad Bookmark list' (createNote method changed to allow that)

-- *** Make changes in this section, only ***

-- title for dialog prompts
property scriptTitle : "Import Delicious bookmarks into Evernote"
-- set the variable below to the name of the Evernote folder to use for imported bookmarks
property evNotebook : "Delicious Import"

-- note for bad bookmarks - local only (won't be synchronised!)
property badNotebook : "Bad Bookmark List"

-- *** Stop editing here, please 🙂 ***

-- firstly, create the 'bad' notebook
-- we do this now because otherwise AppleScript will, by default, create a synchronised notebook (bad idea if heaps of your bookmarks are invalid)
-- also, an error will be thrown if the notebook already exists - to prevent that, we do a quick check to see if a matching notebook can be found
tell application "Evernote"
	if (not (notebook named badNotebook exists)) then
		create notebook badNotebook with type local only
	end if
end tell

-- prompt the user for their location
global formatDates
property locationOptions : {"U.S.", "Japan", "Outside Japan/U.S."}
set location to (choose from list locationOptions with prompt "Please choose your location:" with title scriptTitle) as string

if location = "U.S." then
	set formatDates to "YES"
else if (location = "Japan") then
	set formatDates to "JP"
else
	set formatDates to "NO"
end if

-- verify URLs?
-- if the user responds in the affirmative, all URLs will be verified as working before they're added as notes
-- if the users responds in the negative, URLs are not verified and will be added even if they don't work
global verify
property verifyOptions : {"Yes - Verify all URLs", "No - Just import everything"}
set verifyResult to (choose from list verifyOptions with prompt "Verify URLs before import?" with title scriptTitle) as string

if verifyResult = "Yes - Verify all URLs" then
	set verify to true
else
	set verify to false
end if

-- use bookmark title or URL as new note's title?
-- the original script added notes with the bookmark's URL as the note title
-- this will allow the user to keep this option, if they like, or have the notes' title as the bookmark title (better, in my opinion)
global titleType
property titleOptions : {"Use bookmark's URL", "Use bookmark's title"}
set titleResult to (choose from list titleOptions with prompt "Use bookmark URL or title as note title?" with title scriptTitle) as string

if titleResult = "Use bookmark's URL" then
	set titleType to "url"
else
	set titleType to "title"
end if

-- save the page's full content as the note's content?
-- if the user responds in the affirmative, the page's entire content will be scraped and saved as the note's body
-- this is recommended for Evernote premium users, only
global contentType
property pageOptions : {"Yes - Save full page", "No - Save summary only"}
set pageResult to (choose from list pageOptions with prompt "Save full page as note's content?  This is recommended for Evernote premium users or small bookmark lists, only" with title scriptTitle) as string

if pageResult = "Yes - Save full page" then
	set contentType to "full"
else
	set contentType to "summary"
end if

property linkList : {}
property theTags : {}
property linkExists : ""
property badList : {}

-- Main program begins

-- reset items
set AppleScript's text item delimiters to ""
set linkList to {}
set badList to {}

-- select the Delicious export file
set deliciousFile to (choose file with prompt "Choose Delicious Bookmark Export File")

-- open the Delicious export file
set deliciousData to read deliciousFile
set deliciousBody to my extractBetween(deliciousData, "<dl>", "</dl>")

-- get a list of all the links
set delDelimiter to "<dt>"
set deliciousRAW to read deliciousFile using delimiter delDelimiter
my processLinks(deliciousRAW, linkList)

-- process each link into a seperate webclipped note in Evernote, keeping tags and dates intact
my evernoteImport(linkList, theTags)

-- add bad links to a note containing all the bad URLs
if badList is not {} then

	set old_delim to AppleScript's text item delimiters
	set AppleScript's text item delimiters to return
	set badList to badList as text
	set AppleScript's text item delimiters to old_delim
	set badLinks to "List of URLs Returning Errors: " &amp; return &amp; (current date) &amp; return &amp; return &amp; badList as Unicode text

	tell application "Evernote"
		create note with text badLinks title "List of URLs Returning Errors" notebook evNotebook
	end tell
end if

-- child methods

-- get the anchor href info
on processLinks(deliciousRAW, linkList)
	repeat with i from 1 to (length of deliciousRAW)
		set theItem to item i of deliciousRAW
		if theItem contains "A HREF" then
			set end of linkList to theItem
		end if
	end repeat
end processLinks

-- process each link
on evernoteImport(linkList, theTags)
	repeat with i from 1 to (length of linkList)
		set theItem to item i of linkList
		set theURL to my extractBetween(theItem, "A HREF="", """)
		set linkExists to "true" as boolean
		set epochseconds to my extractBetween(theItem, "ADD_DATE="", """)
		set deliciousTags to my extractBetween(theItem, "TAGS="", """)
		set itemTitle to my extractBetween(theItem, "">", "</A>")
		-- change the delimiters to parse tags
		set oldDelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to ","
		set theTags to text items of deliciousTags as list
		-- change the delimiters back to what they were before
		set AppleScript's text item delimiters to oldDelims

		-- if the user selected to verify all URLs, the URL will be verified here
		if verify then
			my verify_Link(theURL)
		else
			set linkExists to true
		end if

		if linkExists is not false then
			-- add the bookmark to the good list
			if titleType = "title" then
				my createNote(itemTitle, theURL, theTags, epochseconds, evNotebook)
			else
				my createNote(theURL, theURL, theTags, epochseconds, evNotebook)
			end if
			set theTags to {}
		else
			-- add the bookmark to the bad list
			if titleType = "title" then
				my createNote(itemTitle, theURL, theTags, epochseconds, badNotebook)
			else
				my createNote(theURL, theURL, theTags, epochseconds, badNotebook)
			end if
			set theTags to {}
		end if

		if verify then
			-- delay is only required if we're verifying all links
			delay 3
		end if

	end repeat
end evernoteImport

-- verify working URL, if required
on verify_Link(theURL)
	try
		set the_page to do shell script "curl -siL "" &amp; theURL &amp; """
		set the_code to items 10 thru 12 of the_page as string
		if the_code is greater than 400 then
			set linkExists to "false" as boolean
			copy theURL to the end of badList
		end if
	end try
end verify_Link

-- create the note
on createNote(itemTitle, theURL, theTags, epochseconds, notebookName)
	tell application "Evernote"
		try
			set createDate to my epoch2datetime(epochseconds)
			-- create the note based on user's choice of summary or entire page content
			if contentType = "full" then
				-- save entire page (this is the original script)
				set n to create note from url theURL created createDate notebook notebookName tags theTags
				set source URL of n to theURL
			else
				-- save summary only (new option, good for Evernote basic account holders)
				set n to create note title itemTitle with text theURL created createDate tags theTags notebook notebookName
				set source URL of n to theURL
			end if
		end try
	end tell
end createNote

-- convert UNIX epoch time
on epoch2datetime(epochseconds)
	-- adapted from script found at Erik's Lab (http://erikslab.com/2006/09/05/how-to-convert-an-epoch-time-to-a-meaningful-date-and-time/)
	set myshell1 to "date -r "
	if formatDates is "YES" then
		-- formatted for U.S.
		set myshell2 to " "+%m/%d/%Y %l:%M %p"" -- Formatted for U.S.
	else if formatDates is "JP" then
		-- formatted for Japan
		set myshell2 to " "+%Y/%m/%d %l:%M %p""
	else
		-- formatted for everyone outside Japan/U.S.
		set myshell2 to " "+%d/%m/%Y %l:%M %p""
	end if
	set theDatetime to do shell script (myshell1 &amp; epochseconds &amp; myshell2)
	return date theDatetime
end epoch2datetime

-- extract text between specified delimiters
on extractBetween(SearchText, startText, endText)
	set tid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to startText
	set endItems to text of text item -1 of SearchText
	set AppleScript's text item delimiters to endText
	set beginningToEnd to text of text item 1 of endItems
	set AppleScript's text item delimiters to tid
	return beginningToEnd
end extractBetween

Thanks to Justin from Veritrope for the original idea.