Python 网站编码正确(Beautiful Soup)

Python correct encoding of Website (Beautiful Soup)

我正在尝试加载 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

你犯了两个错误;您错误地处理了编码,并且将结果列表视为可以安全地转换为字符串而不会丢失信息的东西。

首先,不要使用response.text!这不是 BeautifulSoup 的错误,您正在重新编码 Mojibake。当服务器未明确指定编码时,requests 库将默认为 text/* 内容类型使用 Latin-1 编码,因为 HTTP 标准规定这是默认值。

参见 Encoding section of the Advanced documentation:

The only time Requests will not do this is if no explicit charset is present in the HTTP headers and the Content-Type header contains text. In this situation, RFC 2616 specifies that the default charset must be ISO-8859-1. Requests follows the specification in this case. If you require a different encoding, you can manually set the Response.encoding property, or use the raw 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
parser = 'html.parser'  # or lxml or html5lib
soup = BeautifulSoup(r.content, parser, from_encoding=encoding)

最后但同样重要的是,使用 BeautifulSoup 4,您可以使用 soup.get_text():

从页面中提取所有文本
text = soup.get_text()
print text

您正在将 结果列表 soup.findAll() 的 return 值)转换为字符串。这永远行不通,因为 Python 中的容器在列表中的每个元素上使用 repr() 来生成 调试字符串 ,对于字符串,这意味着您可以获得转义序列任何不是可打印的 ASCII 字符。

这不是 BeautifulSoup 的错。在使用 BeautifulSoup 之前,您可以通过打印出 encodedText 来看到这一点:非 ASCII 字符已经是乱码了。

这里的问题是您混淆了字节和字符。要更好地了解差异,请阅读 one of Joel's articles,但要点是字节是字节(8 位组,没有任何附加含义),而字符是组成文本字符串的东西。 编码将字符转换为字节,解码将字节转换回字符。

查看 requests documentation 表明 r.text 个字符 组成,而不是字节。你不应该编码它。如果您尝试这样做,您将生成一个字节字符串,当您尝试将其视为字符时,就会发生不好的事情。

有两种方法可以解决这个问题:

  1. 使用存储在 r.content 中的原始未解码字节。然后你可以自己解码把它们变成字符。
  2. requests进行解码,但要确保它使用正确的编解码器。因为您知道在这种情况下是 UTF-8,所以您可以设置 r.encoding = 'utf-8'。如果你在之前访问r.text,那么当你访问r.text时,它就会被正确解码,你会得到一个字符串。您根本不需要弄乱字符编码。

顺便说一句,Python 3 使维护字符串和字节串之间的区别变得更容易一些,因为它要求您使用不同类型的对象来表示它们。

您的代码中有几个错误:

  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")
    
  2. 其次,您遇到了编码问题。您可能正在尝试在终端上可视化结果。对于不在 ASCII 集中的每个字符,您将获得文本中字符的 unicode 表示。您可以这样查看结果:

    res = [item.encode("ascii","ignore") for item in soup.find_all(text=True)]