]> git.phdru.name Git - bookmarks_db.git/blob - parse_html/bkmk_ph_beautifulsoup4.py
Fix(parse_html): Fix import
[bookmarks_db.git] / parse_html / bkmk_ph_beautifulsoup4.py
1 """HTML Parser using BeautifulSoup4
2
3 This file is a part of Bookmarks database and Internet robot.
4
5 """
6
7 __author__ = "Oleg Broytman <phd@phdru.name>"
8 __copyright__ = "Copyright (C) 2017-2023 PhiloSoft Design"
9 __license__ = "GNU GPL"
10
11 __all__ = ['parse_html']
12
13
14 from bs4 import BeautifulSoup
15
16 from .bkmk_ph_util import HTMLParser
17 from compat import string_type
18
19 universal_charset = "utf-8"
20 DEFAULT_CHARSET = "cp1251"  # Stupid default for Russian Cyrillic
21
22
23 def _parse_html(html_text, charset):
24     try:
25         return BeautifulSoup(html_text, from_encoding=charset)
26     except TypeError:
27         return None
28
29
30 def parse_html(html_text, charset=None, log=None):
31     root = _parse_html(html_text, charset)
32     if root is None:
33         return None
34
35     _charset = root.originalEncoding
36     html = root.html
37     if html is None:
38         html = root
39
40     head = html.head
41     if head is None:
42         head = html  # Some sites put TITLE in HTML without HEAD
43
44     title = head.title
45     if (title is None) and (html is not head):
46         # Some sites put TITLE in HTML outside of HEAD
47         title = html.title
48
49     if title is None:
50         # Lookup TITLE in the root
51         title = root.title
52
53     if title is not None:
54         if title.string:
55             title = title.string
56         else:
57             parts = []
58             for part in title:
59                 if not isinstance(part, string_type):
60                     part = part.decode()
61                 parts.append(part.strip())
62             title = ''.join(parts)
63
64     meta = head.find(_find_contenttype, recursive=False)
65     if meta:
66         try:
67             meta_content = meta.get("content")
68             if meta_content:
69                 __charset = meta_content.lower().split('charset=')[1].\
70                     split(';')[0]
71             else:
72                 __charset = False
73         except IndexError:  # No charset in the META Content-Type
74             meta_charset = False
75         else:
76             meta_charset = _charset = __charset
77     else:
78         meta_charset = False
79
80     if not meta_charset:
81         meta = head.find(_find_charset, recursive=False)
82         if meta:
83             meta_content = meta.get("charset")
84             if meta_content:
85                 meta_charset = _charset = meta_content.lower()
86
87     if title and (_charset or meta_charset):
88         try:
89             title = title.encode(_charset or meta_charset)
90         except LookupError:
91             title = title.encode(universal_charset)
92             _charset = universal_charset
93
94     meta = head.find(_find_refresh, recursive=False)
95     if meta:
96         refresh = meta.get("content")
97     else:
98         refresh = None
99
100     meta = head.find(_find_icon, recursive=False)
101     if meta:
102         icon = meta.get("href")
103     else:
104         icon = None
105
106     if (title is None) and (refresh is None) and (icon is None):
107         return None
108     return HTMLParser(_charset, meta_charset, title, refresh, icon)
109
110
111 def _find_contenttype(Tag):
112     return (Tag.name == "meta") and \
113        (Tag.get_attribute_list("http-equiv", '')[0].lower() == "content-type")
114
115
116 def _find_charset(Tag):
117     return (Tag.name == "meta") and Tag.get("charset", '')
118
119
120 def _find_refresh(Tag):
121     return (Tag.name == "meta") and \
122        (Tag.get_attribute_list("http-equiv", '')[0].lower() == "refresh")
123
124
125 def _find_icon(Tag):
126     return (Tag.name == "link") and \
127        (Tag.get_attribute_list("rel", '')[0].lower()
128         in ('icon', 'shortcut icon'))