Jump to content
iwqnna

windows Tool: Import Chrome (Netscape) bookmarks to Evernote

Recommended Posts

Hi, I wanted to organize my bookmarks with tags, and was not happy with the Chrome bookmark manager, so I decided to create a small tool to help importing bookmarks from Chrome (Netscape Bookmark format).

It will parse the bookmark export, create notes with folders as tags. The output xml can be saved as an .enex file and imported to Evernote.

I'm posting it here in case anyone else will find it useful.

I've used simple javascript+jquery parsing functions. It is not tested much.

I was inspired by @gufertum's solution (http://discussion.ev...er-to-evernote/), which however was done in perl, and it did not handle folders and dates

update 2012/12/12: fixed version here

http://jsfiddle.net/iwqnna/LQ2nQ/

  • Like 3

Share this post


Link to post

Hi, I wanted to organize my bookmarks with tags, and was not happy with the Chrome bookmark manager, so I decided to create a small tool to help importing bookmarks from Chrome (Netscape Bookmark format).

It will parse the bookmark export, create notes with folders as tags. The output xml can be saved as an .enex file and imported to Evernote.

I'm posting it here in case anyone else will find it useful.

I've used simple javascript+jquery parsing functions. It is not tested much.

http://jsfiddle.net/kT4nj/

Hi. Welcome to the forum and thanks for posting that. I'll give it a try. Would you mind sharing a note, or a screenshot of it, so that we can see the final product?

Share this post


Link to post

Hi, it's just a simple html page with javascript. I uploaded it to jsfiddle to make sharing easy. Just click the link, click "run" on the and you'll see two input boxes - that's all that is to it

:-)

Share this post


Link to post

Hi, it's just a simple html page with javascript. I uploaded it to jsfiddle to make sharing easy. Just click the link, click "run" on the and you'll see two input boxes - that's all that is to it

:-)

Thanks. I'm out of the house on my Mac today, and I was thinking it might be good for others who wander into this thread to see what it will look like before they do it. I'll give it a try when I am back home in the evening.

Share this post


Link to post

How cool :) Thanks for posting!

Share this post


Link to post

Update: I've noticed there are some bugs.

The date/time should not be formatted with separators, it looks like all times are set to now on import.

I've noticed that Firefox's export format is slightly different, with a <DD> with site description and also modified date/time. It should be possible to include this info with a few minor tweaks to the javascript.

Please also look at hiw I've implemented folder-tag conversion. All notes are set with all parent folders as tags, and all tags are concatenated folder names with 'root' at the bottom. I did it this way to be sure that I did not lose any information, and preserve a kind of namespace for the imported bookmarks vs existing tags/notes.

Share this post


Link to post

This is great, thanks. I also have notes attached to many of my individual bookmarks (as they originally came from Delicious). Do you know how I could use your import tool to maintain this additional info as well? In the html file I am importing from (exported from Google Bookmarks) the note data comes after the <DD> tag.

e.g. <DT><A HREF="url here" ADD_DATE="date here">Bookmark name here</A><DD>More detailed note about bookmark here

Share this post


Link to post

There also seem to be some issues with syncing notes imported via this script. The activity log says:

21:13:41 [576] 86% Skipping note "note name here" flagged as unsyncable (0x4) local flags (ENML_VALIDATION)

Any chance you could help with this and the above question about adapting this for Google Bookmark's use of the <DD> tag? This is the closest I've come to a satisfactory import solution when running Windows, and I'd really appreciate some help clearing the last hurdle. Many thanks.

Share this post


Link to post

I am not sure if it addresses your problems, but I've got an updated version here, I created it some time ago. It fixes some problems, and as I was thinking on how I wanted to convert the folders to tags, I added options along the way

cheers,

Share this post


Link to post

iwqnna

Thanks so much for creating this conversion tool, it is exactly what I am looking for!

Unfortunately when trying to use the tool I am getting an endless amount of pop up dialog boxes with either chunks of HTML, 'h3' or 'null' warnings. I have tried clicking OK on all but there seems to be thousands, so instead I click 'prevent this page from creating additional dialogs'. The resulting code is what seems to be just an XML header, and the tool says 'parsed: 0 bookmarks in 7034 subfolders'. And no option to save.

I have attached a screen grab of the results. post-108378-0-10078700-1351184610_thumb.

The HTML I am pasting in seems to be the right format as it begins with <!DOCTYPE NETSCAPE-Bookmark-file-1>.

I have tried different browsers but get the same end result.

Any suggestions would be very helpful. Thanks again for the tool and you help with my issue.

Chris

Share this post


Link to post

I've Just thought, could it be that I am doing this on a Mac rather than Windows machine?

I can't think why this would make a difference though?

I can see that there are some debug alerts() left in the code - I removed them (see link @ first posting)

Sorry! This jsfiddele thing did not turn out quite as I expected...

I haven't tested this on a mac, but I think that the Netscape format is fairly cross platform. In any case, it should be easy to change the code to catch variations in the export format. (I think the latest version works with bookmarks exported from Firefox)

q

Share this post


Link to post

Thanks for the update iwqnna.

The dialog boxes have disappeared but the file still doesn't pass anything other than an XML header.

Strangely, when I try your original jsfiddle I get an output as desired but it only imports a few notes before crashing (i'm guess this is due to some of the issues raised as per the above comments).

Thanks again for the help with this, at this stage I am willing to do anything to try and get this to work ;)

If you can get this to work I would so much appreciate it. If it helped I could personal message you the HTML I am working with?

Chris

Share this post


Link to post

Thanks iwqnna,

Just to confirm that I am having the same issues as Chris (and I'm on Windows) - the ZnmKF and ZnmKF/1/ versions of the jsfiddle only return an XML header (with and without dialog boxes, respectively); whereas the original version seems very close to working as expected, bar the two issues mentioned above. Any further advice would be much appreciated. Thanks.

Share this post


Link to post

With the kind help of Chris, which provided test data and lots of feedback, I've sorted out some issues, and I hope it should work now. Link updated.

q

Share this post


Link to post

I just tried it and got "Could not import notes, error:INVALID_ROOT (Error in line 6, token "META")" I left all of the existing settings as-is in the form, and checked all three "Include in note body" boxes.

Share this post


Link to post

I just tried it and got a missing note error, this is the start of the enex file ... any clues?

 

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export.dtd"><en-export export-date="20130406T055438Z" application="Evernote/Windows" version="4.x" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:myCustDate="urn:custDate"><note><title>DBS iBanking</title><content><![CDATA[  <en-note>DBS iBanking<br /><a href="https://internet-banking.dbs.com.sg/IB/Welcome">https://internet-banking.dbs.com.sg/IB/Welcome</a></en-note>]]></content><created>undefined</created><tag>TO FILE</tag><tag>TO FILE::Bookmarks bar</tag><tag>TO FILE::Bookmarks bar::Daily</tag><tag>TO FILE::Bookmarks bar::Daily::Account login</tag><note-attributes><source-url>https://internet-banking.dbs.com.sg/IB/Welcome</source-url></note-attributes></note>

Share this post


Link to post

OK I exported a note from Evernote Windows and opened the enex file, copied the first chunk (that wasn't an actual note) and pasted that into my enex file of exported Chrome bookmarks. 

 

I still get missing note title error. However, when I delete most of the notes and just try with the first note - success! There must be a missing note title somewhere further down - does ANYONE have an idea as to how to find it and fix it? Thank you in advance. 

 

As an aside, Evernote, if it wants more users, should make it dead simple to import bookmarks as notes. I have such a huge chunk of my life (and brain) in my bookmarks that if I cannot find a solution to this, I can't see myself making the switch.

Share this post


Link to post

Sorry to keep spamming this thread - I've been experimenting with chunks of the output text, and have managed to import some. I'm beginning to think the problem is a few rogue bookmarks with either really long names or funny symbols. Can anyone shed light on what symbols would cause an error so I can do a find/replace and fix the problem without having to go through the entire file with a fine toothed comb? (I am not a programmer! This is all really painful for me)

Share this post


Link to post

Here is what I did to import from Chrome (my first attempt did not work, because a BOM was getting injected into the embedded CDATA document) :

using System;using System.Collections;using System.Collections.Generic;using System.Text;using System.Xml;using System.IO;using System.Linq;using System.Text.RegularExpressions;using HtmlAgilityPack;namespace ConvertChromeBookmarks{  class Program  {    public static DateTime FromUnixTime(long unixTime)    {      var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);      return epoch.AddSeconds(unixTime);    }    class Bookmark    {      public string name;      public string url;      public DateTime addDate;      public HashSet<List<string>> paths = new HashSet<List<string>>();      public void merge(Bookmark       {        addDate = (addDate > b.addDate) ? addDate : b.addDate;        Console.WriteLine("Same bookmark in different folders : {0}:\n" +                          " - {1}\n - {2}", url, paths.First(), b.paths.First());        if(name != b.name)        {          Console.WriteLine("The same link has different bookmark titles : {0}:\n" +                            " - {1}\n - {2}", url, name, b.name);        }        paths.UnionWith(b.paths);      }      public HashSet<string> tags()      {        HashSet<string> tags = new HashSet<string>();        foreach (List<string> path in paths)        {          foreach (string f in path)          {            foreach (string component in f.Split(new char[]{','})) // Evernote doesn't like commas in folder names            {              tags.Add(component);            }          }        }        return tags;      }    }    public static HtmlDocument readBookmarks(FileInfo inputFI)    {      HtmlDocument inFile = new HtmlDocument();      inFile.OptionFixNestedTags = true;      inFile.OptionAutoCloseOnEnd = true;      HtmlNode.ElementsFlags.Add("dt", HtmlElementFlag.Empty | HtmlElementFlag.Closed);      HtmlNode.ElementsFlags.Add("dl", HtmlElementFlag.Empty | HtmlElementFlag.Closed);      //inFile.Load(inputFI.FullName);      byte[] pageBytes = File.ReadAllBytes(inputFI.FullName);      using (var ms = new MemoryStream(pageBytes))      {        inFile.Load(ms);        var metaContentType = inFile.DocumentNode.SelectSingleNode("//meta[@http-equiv='Content-Type']").GetAttributeValue("content", "");        var contentType = new System.Net.Mime.ContentType(metaContentType);        ms.Position = 0;        inFile.Load(ms, Encoding.GetEncoding(contentType.CharSet));      }      return inFile;    }    public static Hashtable extractLinks(HtmlDocument inFile)    {      Hashtable links = new Hashtable();      foreach (HtmlNode link in inFile.DocumentNode.SelectNodes("//a[@href]"))      {        HtmlAttribute att = link.Attributes["href"];        string addDateStr = link.Attributes["ADD_DATE"].Value;        DateTime addDateDt = FromUnixTime(long.Parse(addDateStr)).ToUniversalTime();        Bookmark b = new Bookmark { name = HtmlEntity.DeEntitize(att.OwnerNode.InnerText), url = att.Value, addDate = addDateDt };        List<string> folders = new List<string>();        foreach (HtmlNode dl in link.SelectNodes("ancestor::dl"))        {          HtmlNode h3 = dl.PreviousSibling.PreviousSibling;          if (h3 == null) continue;          if (string.Compare(h3.Name, "H3", true) != 0) continue;          if (string.Compare(h3.InnerText, "Bookmarks bar", true) == 0) continue;          folders.Add(h3.InnerText);        }        b.paths.Add(folders);        Bookmark b2 = (Bookmark)(links[b.url]);        if (b2 != null)          b2.merge(;        else          links.Add(b.url, ;      }      return links;    }    public static void saveFile(XmlDocument outFile, string filename)    {      using (StreamWriter sw = new StreamWriter(filename, false, System.Text.Encoding.UTF8))      {        outFile.Save(sw);      }    }    static void Main(string[] args)    {      FileInfo inputFI = new FileInfo(args[0]);      if(!inputFI.Exists)        throw new FileNotFoundException("File not found", args[0]);      HtmlDocument inFile = readBookmarks(inputFI);      Hashtable links = extractLinks(inFile);      XmlDocument outFile = new XmlDocument();      outFile.XmlResolver = null;      string dtdLink = "http://xml.evernote.com/pub/evernote-export.dtd";      XmlDocumentType docType = outFile.CreateDocumentType("en-export", null, dtdLink, null);      outFile.AppendChild(docType);      XmlElement root = outFile.CreateElement( "", "en-export", "" );      outFile.AppendChild( root );            DateTime fileCreation = inputFI.CreationTimeUtc;      string fileCreationIso = fileCreation.ToString("yyyyMMddTHHmmssZ");      root.SetAttribute("export-date", fileCreationIso);      root.SetAttribute("application", "Evernote/Windows");      root.SetAttribute("version", "4.x");      foreach(Bookmark b in links.Values)      {        addBookmark(b, outFile, root);      }      saveFile(outFile, Path.GetFileNameWithoutExtension(inputFI.FullName) + ".enex");    }    private static void addBookmark(Bookmark b, XmlDocument outFile, XmlElement root)    {      XmlElement note = outFile.CreateElement( "", "note", "" );      root.AppendChild(note);      XmlElement title = outFile.CreateElement( "", "title", "" );      title.AppendChild(outFile.CreateTextNode(b.name));      note.AppendChild(title);      XmlElement content = outFile.CreateElement( "", "content", "" );      note.AppendChild(content);      XmlDocument subDoc = new XmlDocument();      subDoc.XmlResolver = null;      string dtdLink2 = "http://xml.evernote.com/pub/enml2.dtd";      XmlDocumentType docType2 = subDoc.CreateDocumentType("en-note", null, dtdLink2, null);      subDoc.AppendChild(docType2);      XmlElement root2 = subDoc.CreateElement( "", "en-note", "" );      //root2.SetAttribute("style", "word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;");      //root2.AppendChild(subDoc.CreateTextNode(att.OwnerNode.InnerText));      subDoc.AppendChild( root2 );      XmlElement anchor = subDoc.CreateElement("a");      anchor.SetAttribute("href", b.url);      anchor.AppendChild(subDoc.CreateTextNode(b.url));      root2.AppendChild(anchor);      MemoryStream memoryStream = new MemoryStream();      using (TextWriter writer = new StreamWriter(memoryStream, new UTF8Encoding(false)))      {        XmlWriterSettings writerSettings = new XmlWriterSettings();        writerSettings.OmitXmlDeclaration = false;        //writerSettings.Encoding = Encoding.UTF8;        using (XmlWriter xmlWriter = XmlWriter.Create(writer, writerSettings))        {          subDoc.WriteTo(xmlWriter);          xmlWriter.Flush();          XmlCDataSection cdata = outFile.CreateCDataSection(Encoding.UTF8.GetString(memoryStream.ToArray()));          content.AppendChild(cdata);        }      }      XmlElement created = outFile.CreateElement("", "created", "");      string addDateIso = b.addDate.ToString("yyyyMMddTHHmmssZ");      created.AppendChild(outFile.CreateTextNode(addDateIso));      note.AppendChild(created);      foreach (string tag in b.tags())      {        XmlElement tagElement = outFile.CreateElement("", "tag", "");        tagElement.AppendChild(outFile.CreateTextNode(tag));        note.AppendChild(tagElement);      }      XmlElement noteAttributes = outFile.CreateElement("", "note-attributes", "");      XmlElement sourceUrl = outFile.CreateElement("", "source-url", "");      sourceUrl.AppendChild(outFile.CreateTextNode(b.url));      noteAttributes.AppendChild(sourceUrl);      note.AppendChild(noteAttributes);    }  }}
Edited by Dan0

Share this post


Link to post

The tool is cool, but all the notes were unsyncable. The problem is the CDATA format, which needs to be modified as described here:

http://discussion.evernote.com/topic/38608-enex-file-format-issue/

Specifically, the CDATA needs to be of the format:

<![CDATA[<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE en-note SYSTEM 'http://xml.evernote.com/pub/enml2.dtd'>

 

It shouldn't be too hard to change the script to add the xml and DOCTYPE info, but I just used an editor.

 

(There was also a problem with a few notes with empty <title></title>, but that was easy to fix.)

 

Ken

Share this post


Link to post

Hi Jonathan, I have seen you released an updated version of iwqnna's script, that addresses the syncing problem. The thing is when i execute your script the evernote desktop software (i'm using it on a mac) throws me an "import failed" error message, something that doesn't happens with iwqnna's script. 

why is this happening? i don't know how to code, so i merely understand this matter superficially. 

do you have an updated version? 

thanks in advance.

Share this post


Link to post

×
×
  • Create New...