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