Skip to content

Commit b6a5cbb

Browse files
committed
refactor: improve encoding handling in BeautifulSoup parsing
1 parent 94e4118 commit b6a5cbb

1 file changed

Lines changed: 42 additions & 28 deletions

File tree

apps/common/utils/fork.py

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -184,37 +184,51 @@ def reset_beautiful_soup(self, bf: BeautifulSoup):
184184

185185
@staticmethod
186186
def get_beautiful_soup(response):
187-
encoding = (
188-
response.encoding
189-
if response.encoding is not None and response.encoding != "ISO-8859-1"
190-
else response.apparent_encoding
191-
)
192-
html_content = response.content.decode(encoding)
193-
beautiful_soup = BeautifulSoup(html_content, "html.parser")
194-
meta_list = beautiful_soup.find_all("meta")
195-
charset_list = Fork.get_charset_list(meta_list)
196-
if len(charset_list) > 0:
197-
charset = charset_list[0]
198-
if charset != encoding:
199-
try:
200-
html_content = response.content.decode(charset, errors="replace")
201-
except Exception as e:
202-
maxkb_logger.error(f"{e}: {traceback.format_exc()}")
203-
return BeautifulSoup(html_content, "html.parser")
204-
return beautiful_soup
187+
encoding_list = Fork.get_encoding_list(response)
188+
for encoding in encoding_list:
189+
try:
190+
return BeautifulSoup(response.content.decode(encoding), "html.parser")
191+
except (LookupError, UnicodeDecodeError):
192+
continue
193+
194+
fallback_encoding = encoding_list[0] if len(encoding_list) > 0 else "utf-8"
195+
html_content = response.content.decode(fallback_encoding, errors="replace")
196+
return BeautifulSoup(html_content, "html.parser")
197+
198+
@staticmethod
199+
def get_encoding_list(response):
200+
charset_list = Fork.get_charset_list(response.content)
201+
if response.encoding is not None and response.encoding != "ISO-8859-1":
202+
charset_list.append(response.encoding)
203+
if response.apparent_encoding is not None:
204+
charset_list.append(response.apparent_encoding)
205+
result = []
206+
for charset in charset_list:
207+
normalized_charset = Fork.normalize_charset(charset)
208+
if normalized_charset is not None and normalized_charset not in result:
209+
result.append(normalized_charset)
210+
return result
205211

206212
@staticmethod
207-
def get_charset_list(meta_list):
213+
def get_charset_list(content):
208214
charset_list = []
209-
for meta in meta_list:
210-
if meta.attrs is not None:
211-
if "charset" in meta.attrs:
212-
charset_list.append(meta.attrs.get("charset"))
213-
elif meta.attrs.get("http-equiv", "").lower() == "content-type" and "content" in meta.attrs:
214-
match = re.search(r"charset=([^\s;]+)", meta.attrs["content"], re.I)
215-
if match:
216-
charset_list.append(match.group(1))
217-
return charset_list
215+
content_head = content[:8192]
216+
charset_list.extend(re.findall(rb"<meta[^>]+charset=['\"]?\s*([a-zA-Z0-9._-]+)", content_head, re.I))
217+
charset_list.extend(
218+
re.findall(rb"<meta[^>]+content=['\"][^'\"]*charset=([a-zA-Z0-9._-]+)", content_head, re.I)
219+
)
220+
return [
221+
charset.decode("ascii", errors="ignore")
222+
for charset in charset_list
223+
if len(charset) > 0
224+
]
225+
226+
@staticmethod
227+
def normalize_charset(charset):
228+
if charset is None:
229+
return None
230+
normalized_charset = charset.strip().strip("\"'").lower()
231+
return normalized_charset if len(normalized_charset) > 0 else None
218232

219233
@staticmethod
220234
def _sandbox_requests_get(base_fork_url: str, headers: dict):

0 commit comments

Comments
 (0)