Python BeautifulSoup 4将HTML实体转换为unicode,但在使用打印时获取垃圾字符

Python BeautifulSoup 4将HTML实体转换为unicode,但在使用打印时获取垃圾字符,python,unicode,beautifulsoup,html-entities,Python,Unicode,Beautifulsoup,Html Entities,我正在尝试使用Beautifulsoup4从web上抓取文本来解析它。我在将bs4处理过的文本打印到控制台时遇到问题。每当我碰到一个原本是HTML实体的字符时,比如’;控制台上有垃圾字符。我相信bs4正在正确地将这些实体转换为unicode,因为如果我尝试使用另一种编码来打印文本,它会抱怨字符(如u'\u2019)缺少unicode映射。我不确定为什么打印函数会对这些字符感到困惑。我尝试过改变字体,这会改变垃圾字符,我在Windows7机器上使用的是美国英语语言环境。这里是我的代码供参

我正在尝试使用Beautifulsoup4从web上抓取文本来解析它。我在将bs4处理过的文本打印到控制台时遇到问题。每当我碰到一个原本是HTML实体的字符时,比如’;控制台上有垃圾字符。我相信bs4正在正确地将这些实体转换为unicode,因为如果我尝试使用另一种编码来打印文本,它会抱怨字符(如u'\u2019)缺少unicode映射。我不确定为什么打印函数会对这些字符感到困惑。我尝试过改变字体,这会改变垃圾字符,我在Windows7机器上使用的是美国英语语言环境。这里是我的代码供参考,任何帮助都是感激的。提前谢谢

#!/usr/bin/python
import json
import urllib2
import cookielib
import bs4

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

url = "http://api.nytimes.com/svc/search/v2/articlesearch.json?q=Tiguan\
&page=0&api-key=blah"
response = opener.open(url)
articles = response.read()
decoded = json.loads(articles)

totalpages = decoded['response']['meta']['hits']/10

for page in range(totalpages + 1):
    if page>0:
        url = "http://api.nytimes.com/svc/search/v2/articlesearch.json?\
q=Tiguan&page=" + str(page) + "&api-key=blah"
        response = opener.open(url)
        articles = response.read()
        decoded = json.loads(articles)
    for url in decoded['response']['docs']:
        print url['web_url']
        urlstring = url['web_url']
        art = opener.open(urlstring)
        soup = bs4.BeautifulSoup(art.read())
        goodstuff = soup.findAll('nyt_text')
        for tag in goodstuff:
            print tag.prettify().encode("UTF")

这个问题与bs4、HTML实体或其他任何东西无关。您可以在大多数Windows系统上复制完全相同的行为,使用一行程序打印出当您尝试打印时显示为垃圾的相同字符,如下所示:

print u'\u2019'.encode('UTF-8')
这里的问题是,与绝大多数Windows系统一样(2013年没有其他人使用),默认字符集不是UTF-8,而是类似CP1252的字符集

因此,当您将Unicode字符串编码为UTF-8并将这些字节打印到控制台时,控制台将它们解释为CP1252。在本例中,这意味着您将获得
€™而不是

改变字体也无济于事。
\u2013
的UTF-8编码是三个字节
\xe2
\x80
、和
\x99
,这三个字节的CP1252含义是

如果要为控制台手动编码,则需要编码到正确的字符集,即控制台实际使用的字符集。您可以将其作为
sys.stdout.encoding
获取

当然,您在尝试为正确的字符集编码时可能会遇到异常,因为像CP1252这样的8位字符集在Unicode中只能处理110K字符中的240个。唯一的处理方法是使用
errors
参数
encode
忽略它们或用替换字符替换它们

同时,如果你还没有读过这本书,你真的需要。尤其是如果您打算坚持使用Python2.x和Windows


如果您想知道为什么一些命令行程序似乎能够解决这些问题:Microsoft的字符集问题解决方案是创建一整套使用16位字符而不是8位字符的并行API,并且这些API始终使用UTF-16。不幸的是,很多东西,比如微软提供的用于与控制台对话的可移植stdio包装器,以及python2.x所依赖的可移植stdio包装器,都只有8位API。这意味着问题根本没有解决。Python3.x不再使用这些包装器,关于让未来版本的UTF-16与控制台对话的讨论也不断出现。但是,即使这发生在3.4中(这似乎不太可能),只要您使用2.x,这也不会对您有所帮助。

包含了对该问题的良好解释

在您的特定情况下,您只需将
编码
参数传递给
prettify()
,而不是默认的
utf-8

如果要打印到控制台,可以尝试直接打印Unicode:

print soup.prettify(encoding=None, formatter='html') # print Unicode
。如果您通过了ascii
;然后,
BeautifulSoup
可以使用数字字符引用而不是非ascii字符:

print soup.prettify('ascii', formatter='html')
它假设当前的Windows代码页是基于ascii的编码(大多数都是这样)。如果输出通过管道重定向到一个文件或另一个程序,它也应该工作


为了便于携带,您可以始终打印Unicode(
encoding=None
以上)并使用
PYTHONIOENCODING
获得适当的字符编码,例如文件、管道的utf-8和
ascii:xmlcharrefreplace
,以避免控制台中的垃圾。

相关:顺便说一句,您不在@塞巴斯蒂安:我已经有好几次几乎把一个问题标为一个重复的问题,但它充满了看起来正确而不正确的答案。我们真的需要一个地方来收集所有不同的笨拙的解决方案,解释每个解决方案的问题,并明确指出,除非您停止使用Windows或Python 2.x,否则这些笨拙的解决方案将与您得到的一样好…@abarnert:通常我只打印Unicode并设置适当的
PYTHONIOENCODING
,例如。,utf-8用于文件、管道和
ascii:xmlcharrefreplace
,以避免控制台中的垃圾。@J.F.Sebastian:嗯,
xmlcharrefreplace
并不完全是终端用户可读/友好的。无论如何,一些Python的未来版本将使用类似于该问题的Windows控制台对象(但与
io
模块正确集成)的东西在Windows上执行UTF-16输出,之后问题将消失(除了一些旧的和专门的Unix系统上的人,他们往往知道自己在做什么或不关心什么)。但只要人们坚持使用Python2.x,3.5解决了这个问题就无关紧要了……其结果是将HTML实体以纯文本形式打印到控制台,这可能会带来一个有用的解决方法。谢谢@DaWisePug:命名字符实体是由于
formatter='html'
引起的。如果不需要,请删除它。@DaWisePug:我已经更新了答案,提到了更通用的解决方案:打印Unicode,使用
PYTHONIOENCODING
进行特定的大小写。是的,谢谢您的回复。我尝试设置控制台的代码页,但也没有真正起作用。我对python版本的选择是由我的学校提供的linux服务器的管理员决定的,我最终想在那里运行我的程序。我选择Windows是因为