]> git.phdru.name Git - bookmarks_db.git/blob - Robots/parse_html_beautifulsoup.py
Full name for "IGNORECASE".
[bookmarks_db.git] / Robots / parse_html_beautifulsoup.py
1 """
2    HTML Parser using BeautifulSoup
3
4    Written by BroytMann. Copyright (C) 2007, 2008 PhiloSoft Design
5 """
6
7 import re
8 from sgmllib import SGMLParser, SGMLParseError
9 from HTMLParser import HTMLParser
10 from BeautifulSoup import BeautifulSoup, CData
11
12
13 class BSoupParser(HTMLParser):
14    def __init__(self, charset, meta, title, refresh, icon):
15       object.__init__(self)
16       self.charset = charset
17       self.meta_charset = meta
18       self.title = title
19       self.refresh = refresh
20       self.icon = icon
21
22
23 # http://groups.google.com/group/beautifulsoup/browse_thread/thread/69093cb0d3a3cf63
24 class BadDeclParser(BeautifulSoup):
25     def parse_declaration(self, i):
26          """Treat a bogus SGML declaration as raw data. Treat a CDATA
27          declaration as a CData object."""
28          j = None
29          if self.rawdata[i:i+9] == '<![CDATA[':
30               k = self.rawdata.find(']]>', i)
31               if k == -1:
32                   k = len(self.rawdata)
33               data = self.rawdata[i+9:k]
34               j = k+3
35               self._toStringSubclass(data, CData)
36          else:
37              try:
38                  j = SGMLParser.parse_declaration(self, i)
39              except SGMLParseError:
40                  # Could not parse the DOCTYPE declaration
41                  # Try to just skip the actual declaration
42                  match = re.search(r'<!DOCTYPE([^>]*?)>', self.rawdata[i:], re.MULTILINE|re.IGNORECASE)
43                  if match:
44                      toHandle = self.rawdata[i:match.end()]
45                  else:
46                      toHandle = self.rawdata[i:]
47                  self.handle_data(toHandle)
48                  j = i + len(toHandle)
49          return j
50
51
52 def _parse_html(filename, charset):
53    infile = open(filename, 'r')
54    try:
55       return BadDeclParser(infile, fromEncoding=charset)
56    except TypeError:
57       return None
58    finally:
59       infile.close()
60
61 def parse_html(filename, charset=None, log=None):
62    root = _parse_html(filename, charset)
63    if root is None:
64       return None
65
66    _charset = root.originalEncoding
67    if _charset in ("ISO-8859-2", "windows-1252", "MacCyrillic"): # Replace default
68       _charset = DEFAULT_CHARSET
69       root = _parse_html(filename, _charset)
70       if root is None:
71          return None
72
73    try:
74       html = root.html
75    except AttributeError:
76       if log: log("No HTML in root")
77       html = root
78
79    if html is None:
80       html = root
81
82    try:
83       head = html.head
84    except AttributeError:
85       if log: log("No HEAD in HTML")
86       head = html
87
88    if head is None:
89       head = html # Some sites put TITLE in HTML without HEAD
90
91    try:
92       title = head.title.string.encode(_charset)
93    except AttributeError:
94       title = '' # HEAD but no TITLE
95
96    if (not title) and (head is not html):
97       # Some sites put TITLE in HTML outside of HEAD
98
99       try:
100          title = html.title.string.encode(_charset)
101       except AttributeError:
102          title = '' # no TITLE in HTML too
103
104    meta = head.find(_find_contenttype, recursive=False)
105    if meta:
106       try:
107          __charset = meta.get("content").lower().split('charset=')[1].split(';')[0]
108       except IndexError: # No charset in the META Content-Type
109          meta_charset = False
110       else:
111          meta_charset = _charset == __charset
112    else:
113       meta_charset = False
114
115    meta = head.find(_find_refresh, recursive=False)
116    if meta:
117       refresh = meta.get("content")
118    else:
119       refresh = None
120
121    meta = head.find(_find_icon, recursive=False)
122    if meta:
123       icon = meta.get("href")
124    else:
125       icon = None
126
127    return BSoupParser(_charset, meta_charset, title, refresh, icon)
128
129 def _find_contenttype(Tag):
130    return (Tag.name == "meta") and \
131       (Tag.get("http-equiv", '').lower() == "content-type")
132
133 def _find_refresh(Tag):
134    return (Tag.name == "meta") and \
135       (Tag.get("http-equiv", '').lower() == "refresh")
136
137 def _find_icon(Tag):
138    return (Tag.name == "link") and \
139       (Tag.get("rel", '').lower() in ('icon', 'shortcut icon'))