所有的HTML或XML文档都以某种特定的编码(如ASCII或UTF-8)书写。然而,当你将该HTML/XML文档加载进BeautifulSoup时,它会被转换成Unicode。
示例
from bs4 import BeautifulSoup
markup = "<p>I will display £</p>"
soup = BeautifulSoup(markup, "html.parser")
print(soup.p)
print(soup.p.string)
输出
<p>I will display £</p>
I will display £
上述行为是因为BeautifulSoup内部使用了一个名为Unicode, Dammit
的子库来检测文档的编码,然后将其转换成Unicode。
然而,并非每次Unicode, Dammit
都能正确猜测。由于文档是逐字节地搜索来猜测编码,这需要花费大量时间。如果你已经知道编码,可以通过将它作为from_encoding
参数传递给BeautifulSoup构造函数来节省时间并避免错误。
下面是一个例子,BeautifulSoup错误地将一个ISO-8859-8文档识别为ISO-8859-7:
示例
from bs4 import BeautifulSoup
markup = b"<h1>\xed\xe5\xec\xf9</h1>"
soup = BeautifulSoup(markup, 'html.parser')
print(soup.h1)
print(soup.original_encoding)
输出
<h1>翴檛</h1>
ISO-8859-7
为了解决上述问题,可以使用from_encoding
参数传递给BeautifulSoup:
示例
from bs4 import BeautifulSoup
markup = b"<h1>\xed\xe5\xec\xf9</h1>"
soup = BeautifulSoup(markup, "html.parser", from_encoding="iso-8859-8")
print(soup.h1)
print(soup.original_encoding)
输出
<h1>????</h1>
iso-8859-8
从BeautifulSoup 4.4.0版本开始增加的一个新特性是exclude_encoding
。当不确定正确的编码但确定Unicode, Dammit
显示的结果是错误的情况下,可以使用此特性。
soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])
输出编码
从BeautifulSoup输出的内容是UTF-8文档,无论输入的文档是什么编码。下面是一个包含波兰字符的文档,格式为ISO-8859-2。
示例
markup = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-2">
</HEAD>
<BODY>
? ? ? ? ń ó ? ? ? ? ? ? ? ? Ó ? ? ?
</BODY>
</HTML>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(markup, "html.parser", from_encoding="iso-8859-8")
print(soup.prettify())
输出
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
</head>
<body>
? ? ? ? ń ó ? ? ? ? ? ? ? ? Ó ? ? ?
</body>
</html>
在上面的例子中,如果你注意到,<meta>
标签已被重写以反映BeautifulSoup生成的文档现在是以UTF-8格式的。
如果你不想让生成的输出是UTF-8,可以在prettify()
中指定所需的编码。
print(soup.prettify("latin-1"))
输出
b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n<html>\n <head>\n <meta content="text/html; charset=latin-1" http-equiv="content-type"/>\n </head>\n <body>\n ? ? ? ? ń \xf3 ? ? ? ? ? ? ? ? \xd3 ? ? ?\n </body>\n</html>\n'
在上面的例子中,我们对整个文档进行了编码,但也可以像处理Python字符串那样对soup中的任何特定元素进行编码:
soup.p.encode("latin-1")
soup.h1.encode("latin-1")
输出
b'<p>My first paragraph.</p>'
b'<h1>My First Heading</h1>'
任何不能用所选编码表示的字符将会被转换成数值XML实体引用。下面是一个这样的例子:
markup = u"<b>\N{SNOWMAN}</b>"
snowman_soup = BeautifulSoup(markup)
tag = snowman_soup.b
print(tag.encode("utf-8"))
输出
b'<b>\xe2\x98\x83</b>'
如果你尝试用latin-1
或ascii
编码上述内容,它将生成☃
,表明没有这种表示。
print(tag.encode("latin-1"))
print(tag.encode("ascii"))
输出
b'<b>?</b>'
b'<b>?</b>'
Unicode, Dammit
Unicode, Dammit
主要用于当传入的文档格式未知(主要是外语)并且我们想要将其编码成某种已知格式(如Unicode)的情况,而且我们不需要BeautifulSoup来做这一切。