]> git.phdru.name Git - bookmarks_db.git/blob - bkmk_parser.py
m_lib.defenc is always available.
[bookmarks_db.git] / bkmk_parser.py
1 """Parser for Netscape Navigator's and Mozilla's bookmarks.html
2
3 This file is a part of Bookmarks database and Internet robot.
4 """
5
6 __version__ = "$Revision$"[11:-2]
7 __revision__ = "$Id$"[5:-2]
8 __date__ = "$Date$"[7:-2]
9 __author__ = "Oleg Broytman <phd@phdru.name>"
10 __copyright__ = "Copyright (C) 1997-2011 PhiloSoft Design"
11 __license__ = "GNU GPL"
12
13 __all__ = ['BkmkParser']
14
15
16 import sys, os
17 from m_lib.defenc import default_encoding as DEFAULT_CHARSET
18 from m_lib.net.www.html import HTMLParser
19 from bkmk_objects import Folder, Bookmark, Ruler
20
21
22 DEBUG = os.environ.has_key("BKMK_DEBUG")
23
24 if DEBUG:
25    def debug(note):
26       print note
27
28    def dump_names(folder_stack):
29       l = []
30       for object in folder_stack:
31          if object.isFolder:
32             l.append(object.name)
33       return "'%s'" % "' '".join(l)
34
35 else:
36    def debug(note):
37       pass
38    dump_names = debug
39
40
41 class BkmkParser(HTMLParser):
42    def __init__(self):
43       HTMLParser.__init__(self)
44
45       self.urls = 0
46       self.objects = 0
47
48       self.charset = None
49       self.recode = None
50
51    def handle_data(self, data):
52       if data:
53          if self.charset and DEFAULT_CHARSET:
54             data = unicode(data, self.charset, "replace").encode(DEFAULT_CHARSET, "xmlcharrefreplace")
55          self.accumulator += data
56
57    # Mozilla - get charset
58    def do_meta(self, attrs):
59       http_equiv = ""
60       content = ""
61
62       for attrname, value in attrs:
63          value = value.strip()
64          if attrname == 'http-equiv':
65             http_equiv = value.lower()
66          elif attrname == 'content':
67             content = value
68
69       if http_equiv == "content-type":
70          try:
71             # extract charset from "text/html; charset=UTF-8"
72             self.charset = content.split('=')[1]
73          except IndexError:
74             pass
75
76    def start_title(self, attrs):
77       if DEFAULT_CHARSET:
78          self.accumulator += '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=%s">\n' % DEFAULT_CHARSET
79       self.accumulator += "<TITLE>"
80
81    def end_title(self):
82       self.accumulator += "</TITLE>"
83
84    # Start root folder
85    def start_h1(self, attrs):
86       root_folder = Folder()
87       self.current_object = root_folder
88       self.root_folder = root_folder
89       self.current_folder = root_folder
90       self.folder_stack = [root_folder]
91
92       self.root_folder.header = self.accumulator.strip()
93       self.accumulator = ''
94
95    def end_h1(self):
96       accumulator = self.accumulator
97       self.accumulator = ''
98
99       debug("Root folder name: `%s'" % accumulator)
100       self.root_folder.name = accumulator
101
102    # Start a folder
103    def start_h3(self, attrs):
104       last_modified = None
105       for attrname, value in attrs:
106          value = value.strip()
107          if attrname == 'add_date':
108             add_date = value
109          elif attrname == 'last_modified':
110             last_modified = value
111
112       debug("New folder...")
113       folder = Folder(add_date, last_modified=last_modified)
114       self.current_object = folder
115       self.current_folder.append(folder)
116       self.folder_stack.append(folder) # push new folder
117       self.current_folder = folder
118       self.objects += 1
119
120    def end_h3(self):
121       accumulator = self.accumulator
122       self.accumulator = ''
123
124       debug("Folder name: `%s'" % accumulator)
125       self.current_folder.name = accumulator
126
127    # Start a bookmark
128    def start_a(self, attrs):
129       add_date = None
130       last_visit = None
131       last_modified = None
132       keyword = None
133       icon = None
134       charset = None
135
136       for attrname, value in attrs:
137          value = value.strip()
138          if attrname == "href":
139             href = value
140          elif attrname == "add_date":
141             add_date = value
142          elif attrname == "last_visit":
143             last_visit = value
144          elif attrname == "last_modified":
145             last_modified = value
146          elif attrname == "shortcuturl":
147             keyword = value
148          elif attrname == "icon":
149             icon = value
150          elif attrname == "last_charset":
151             charset = value
152
153       debug("Bookmark points to: `%s'" % href)
154       bookmark = Bookmark(href, add_date, last_visit, last_modified,
155          keyword or '', '', icon, charset)
156       self.current_object = bookmark
157       self.current_folder.append(bookmark)
158       self.urls += 1
159       self.objects += 1
160
161    def end_a(self):
162       accumulator = self.accumulator
163       self.accumulator = ''
164
165       debug("Bookmark name: `%s'" % accumulator)
166       bookmark = self.current_folder[-1]
167       bookmark.name = accumulator
168
169    def flush(self):
170       accumulator = self.accumulator
171
172       if accumulator:
173          self.accumulator = ''
174
175          current_object = self.current_object
176          if current_object:
177             current_object.comment += accumulator.strip()
178             debug("Comment: `%s'" % current_object.comment)
179
180    def start_dl(self, attrs):
181       self.flush()
182
183    do_dt = start_dl
184
185    # End of folder
186    def end_dl(self):
187       self.flush()
188       debug("End folder")
189       debug("Folder stack: %s" % dump_names(self.folder_stack))
190       if self.folder_stack:
191          del self.folder_stack[-1] # pop last folder
192          if self.folder_stack:
193             self.current_folder = self.folder_stack[-1]
194          else:
195             debug("FOLDER STACK is EMPTY!!! (1)")
196       else:
197          debug("FOLDER STACK is EMPTY!!! (2)")
198       self.current_object = None
199
200    def close(self):
201       HTMLParser.close(self)
202       if self.folder_stack:
203          raise ValueError, "wrong folder stack: %s" % self.folder_stack
204
205    def do_dd(self, attrs):
206       pass
207
208    do_p = do_dd
209
210    # Start ruler
211    def do_hr(self, attrs):
212       self.flush()
213       debug("Ruler")
214       self.current_folder.append(Ruler())
215       self.current_object = None
216       self.objects += 1
217
218    # BR in comment
219    def do_br(self, attrs):
220       self.accumulator += "<BR>"
221
222    # Allow < in the text
223    def unknown_starttag(self, tag, attrs):
224       self.accumulator += "<%s>" % tag
225
226    # Do not allow unknow end tags
227    def unknown_endtag(self, tag):
228       raise NotImplementedError("Unknow end tag `%s'" % tag)