从具有重复元标头的网站构造的 BeautifulSoup 对象的矛盾编码。如何确保编码不混淆?

Ambivalent encoding of BeautifulSoup object constructed from website with duplicate meta header. How do I make sure the encoding is not mixed up?

提问人:LLaP 提问时间:10/31/2022 最后编辑:LLaP 更新时间:11/1/2022 访问量:49

问:

我已经使用模块从网站获取了数据。我从元标头中知道本文档的源编码是“iso-8859-1”。我也知道在创建对象时自动转码为“UTF-8”。BeautifulSoupBeutifulSoupBeautifulSoup

import requests
from bs4 import BeautifulSoup

url = "https://www.assemblee-nationale.fr/12/cri/2003-2004/20040001.asp"
r=requests.get(url)
soup_data=BeautifulSoup(r.content, 'lxml')

print(soup_data.prettify())

不幸的是,该网站有一个重复的元素。

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

在使用 prettify 检查对象时,我意识到只转换了其中一个元标记。BeautifulSoupBeautifulSoup

<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta content="text/html; charset=iso-8859-1" http-eqiv="Content-Type"/>

因此,我对我的对象的实际编码是什么感到困惑。BeautifulSoup

此外,在数据处理过程中,我意识到我的 PyCharm 控制台没有正确解析此对象的某些文本元素。这些字符串是“iso-8859-1”代码字符。因此,我怀疑该对象要么仍在 ISO 编码中,要么更糟,不知何故混淆了。

['\xa0\xa0\xa0\xa0M. le président.' '\xa0\xa0\xa0\xa0M. le président.'

我在运行 numpy 函数后第一次看到这些 ISO 字符。

series = np.apply_along_axis(lambda x: x[0].get_text(), 0, [df])

关于如何摆脱这种情况的任何建议?我想将对象转换为 UTF-8(并 100% 确定它完全是 UTF-8)。

python html beautifulsoup 编码 UTF-8

评论

0赞 Mark Tolonen 11/1/2022
假设 Python 3 这些字符串已经解码为 Unicode 码位。 是 U+00A0 不间断空格。字符串根本没有编码。 (注意 b) 是以 ISO-8859-1 编码0xa0字节'\xa0'b'\xa0'
0赞 LLaP 11/1/2022
我现在在您的回答中使用了“from_encoding='Windows-1252'”,但“\xa0”仍在打印出来,而不是不间断空格。有什么建议为什么这个角色仍然没有正确显示吗?
1赞 Mark Tolonen 11/1/2022
在列表中,将显示字符串的调试表示形式。这样您就可以分辨出 ASCII 空格和不间断空格之间的区别。如果单独列出列表中的字符串,它将显示为空格而不是转义码。print
0赞 LLaP 11/2/2022
事实上,我意识到这些“\xa0\”代码点出现在列表中,但不会出现在字符串中。令我感到困惑的是,情况并非总是如此。某些列表(全部来自同一源)显示此代码点,而其他列表则不显示。

答:

0赞 Deneb 10/31/2022 #1

确保使用正确的编码,可以使用 packaged with :EncodingDetectorbs4

import requests
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector

url = "https://www.assemblee-nationale.fr/12/cri/2003-2004/20040001.asp"
r = requests.get(url)

encoding = EncodingDetector.find_declared_encoding(r.content, is_html=True)
soup_data = BeautifulSoup(r.content, "lxml", from_encoding=encoding)

print(soup_data.prettify())

评论

0赞 LLaP 10/31/2022
我知道元标头的实际编码。我试过你的例子,它没有改变任何东西。prettify 打印中的两个元标头仍然显示矛盾的信息,后处理字符串仍然显示一些 ISO 字符。
1赞 Mark Tolonen 11/1/2022 #2

BeautifulSoup使用编码将(对象)解码为Unicode(对象)。A 根本没有编码。它由Unicode码位组成。ISO-8859-1r.contentbytesstrstr

事实证明,数据不是用 ISO-8859-1 编码的。它是用 Windows-1252 编码的,这是一种类似的编码,但有一些额外的翻译(请参阅每个的超链接)。

响应指示使用的网站编码 () 和使用其检测代码 () 的表观编码。以下是我发现的实际文本中的一些差异:requestsr.encodingr.apparent_encoding

import requests
from bs4 import BeautifulSoup

url = "https://www.assemblee-nationale.fr/12/cri/2003-2004/20040001.asp"
r=requests.get(url)
print(f'{r.encoding=}')
print(f'{r.apparent_encoding=}')
print()
soup_data=BeautifulSoup(r.content, 'lxml')
print(repr(soup_data.find('a',href="http://www2.assemblee-nationale.fr/scrutins/liste/(legislature)/15/(type)/AUT").text))
print(repr(soup_data.find('a',href="#",accesskey="0").text))
print()
#Using the correct encoding
soup_data=BeautifulSoup(r.content, 'lxml', from_encoding='Windows-1252')
print(repr(soup_data.find('a',href="http://www2.assemblee-nationale.fr/scrutins/liste/(legislature)/15/(type)/AUT").text))
print(repr(soup_data.find('a',href="#",accesskey="0").text))

输出。请注意“谴责...”中的代码点首先是“d'accessibilité”。ISO-8859-1 中不存在 (U+2026) 和 (U+2019) 代码点,字节 0x85 和 0x92 分别转换为 U+0085 和 U+0092,它们是不可打印的控制代码。我曾经将它们显示为转义码。\x85\x92repr()

r.encoding='ISO-8859-1'
r.apparent_encoding='Windows-1252'

'Autres scrutins solennels (déclarations, motions de censure\x85)'
'Politique d\x92accessibilité'

'Autres scrutins solennels (déclarations, motions de censure…)'
'Politique d’accessibilité'