我正在尝试加载一个html页面并输出文本,即使我正确地获取了网页,BeautifulSoup也会以某种方式破坏编码。
来源:
# -*- coding: utf-8 -*-
import requests
from BeautifulSoup import BeautifulSoup
url = "http://www.columbia.edu/~fdc/utf8/"
r = requests.get(url)
encodedText = r.text.encode("utf-8")
soup = BeautifulSoup(encodedText)
text = str(soup.findAll(text=True))
print text.decode("utf-8")
摘录输出:
...Odenw\xc3\xa4lderisch...
这应该是Odenwälderisch
答案 0 :(得分:24)
你犯了两个错误;您是错误处理编码,并且您将结果列表视为可以安全地转换为字符串而不会丢失信息的内容。
首先,不要使用response.text
!这里不是BeautifulSoup,你正在重新编码Mojibake。当服务器没有明确指定编码时,requests
库将默认为text/*
内容类型的Latin-1编码,因为HTTP标准声明这是默认值。
请参阅Encoding section of the Advanced documentation:
请求不会执行此操作的唯一情况是,如果HTTP标头中没有明确的字符集和,
Content-Type
标头包含text
。 在这种情况下,RFC 2616指定默认字符集必须为ISO-8859-1
。在这种情况下,请求遵循规范。如果您需要不同的编码,可以手动设置Response.encoding
属性,或使用原始Response.content
。
大胆强调我的。
传递response.content
原始数据:
soup = BeautifulSoup(r.content)
我看到你正在使用BeautifulSoup 3.你真的想升级到BeautifulSoup 4;版本3已于2012年停产,并包含多个错误。安装beautifulsoup4
project,然后使用from bs4 import BeautifulSoup
。
BeautifulSoup 4通常可以很好地确定在解析时使用的正确编码,无论是来自HTML <meta>
标记还是对提供的字节的统计分析。如果服务器确实提供了字符集,您仍然可以从响应中将其传递给BeautifulSoup,但如果requests
使用默认值,则先测试:
encoding = r.encoding if 'charset' in r.headers.get('content-type', '').lower() else None
soup = BeautifulSoup(r.content, from_encoding=encoding)
最后但并非最不重要的是,使用BeautifulSoup 4,您可以使用soup.get_text()
从页面中提取所有文字:
text = soup.get_text()
print text
您正在将结果列表(soup.findAll()
的返回值)转换为字符串。这永远不会起作用,因为Python中的容器在列表中的每个元素上使用repr()
来生成调试字符串,对于字符串,这意味着您可以获得任何不是可打印ASCII字符的转义序列。
答案 1 :(得分:4)
这不是BeautifulSoup的错。在使用BeautifulSoup之前,您可以通过打印encodedText
来看到这一点:非ASCII字符已经是乱码。
这里的问题是你要混合字节和字符。为了更好地概述差异,请阅读one of Joel's articles,但要点是字节是,字节(8位的组,没有附加任何其他含义),而字符是组成文本字符串的东西。 编码将字符转换为字节,解码将字节转换回字符。
查看requests
documentation表示r.text
由字符组成,而不是字节。你不应该编码它。如果您尝试这样做,您将创建一个字节字符串,当您尝试将其视为字符时,会发生不好的事情。
有两种方法可以解决这个问题:
r.content
,as Martijn suggested中。然后你可以自己解码它们,把它们变成字符。requests
进行解码,但只是确保它使用正确的编解码器。由于您知道这种情况下的UTF-8,因此您可以设置r.encoding = 'utf-8'
。如果您在之前执行此操作,则访问r.text
,然后当您访问r.text
时,它将被正确解码,并且您将获得一个字符串。你根本不需要搞乱角色编码。顺便说一句,Python 3使得维护字符串和字节字符串之间的差异变得更容易,因为它要求您使用不同类型的对象来表示它们。
答案 2 :(得分:1)
您的代码中存在一些错误:
首先,您不需要尝试重新编码文本。 请求可以为您提供页面的本机编码,BeautifulSoup可以获取此信息并自行进行解码:
# -*- coding: utf-8 -*-
import requests
from BeautifulSoup import BeautifulSoup
url = "http://www.columbia.edu/~fdc/utf8/"
r = requests.get(url)
soup = BeautifulSoup(r.text, "html5lib")
其次,您有编码问题。您可能正在尝试在终端上显示结果。您将得到的是文本中字符的unicode表示,表示不在ASCII集中的每个字符。你可以检查这样的结果:
res = [item.encode("ascii","ignore") for item in soup.find_all(text=True)]