Python和BeautifulSoup编码问题[重复]

Posted

技术标签:

【中文标题】Python和BeautifulSoup编码问题[重复]【英文标题】:Python and BeautifulSoup encoding issues [duplicate] 【发布时间】:2011-11-05 08:52:29 【问题描述】:

我正在使用 BeautifulSoup 用 Python 编写一个爬虫,一切都很顺利,直到我遇到了这个网站:

http://www.elnorte.ec/

我正在使用请求库获取内容:

r = requests.get('http://www.elnorte.ec/')
content = r.content

如果我此时打印内容变量,所有西班牙特殊字符似乎都可以正常工作。但是,一旦我尝试将 content 变量提供给 BeautifulSoup,一切都会变得一团糟:

soup = BeautifulSoup(content)
print(soup)
...
<a class="blogCalendarToday" href="/component/blog_calendar/?year=2011&amp;month=08&amp;day=27&amp;modid=203" title="1009 artículos en este día">
...

它显然把所有的西班牙特殊字符(口音之类的)都弄乱了。我试过做 content.decode('utf-8'), content.decode('latin-1'),还尝试将 fromEncoding 参数设置为 BeautifulSoup,将其设置为 fromEncoding='utf-8' 和 fromEncoding ='latin-1',但还是没有骰子。

任何指针将不胜感激。

【问题讨论】:

【参考方案1】:

在您的情况下,此页面包含错误的 utf-8 数据,这会混淆 BeautifulSoup 并使其认为您的页面使用 windows-1252,您可以这样做:

soup = BeautifulSoup.BeautifulSoup(content.decode('utf-8','ignore'))

通过这样做,您将丢弃页面源中的任何错误符号,BeautifulSoup 将正确猜测编码。

您可以将 'ignore' 替换为 'replace' 并检查文本中的 '?'符号以查看已丢弃的内容。

实际上,编写爬虫是一项非常艰巨的任务,它每次都能以 100% 的机会猜测页面编码(现在的浏览器非常擅长),你可以使用像 'chardet' 这样的模块,但是,例如,在你的情况下会猜测编码为 ISO-8859-2,这也不正确。

如果您确实需要能够为用户可能提供的任何页面获取编码 - 您应该构建一个多级(尝试 utf-8、尝试 latin1、尝试等...)检测功能(就像我们所做的那样在我们的项目中)或使用一些来自 firefox 或 chromium 的检测代码作为 C 模块。

【讨论】:

github.com/LuminosoInsight/python-ftfy 是另一个用于清理 mojibake 的库,得到了好评。 如果您使用的是本地(在我的情况下为 html)文件,那么这可以工作:soup = BeautifulSoup(open("C:\\path\\to\\your\\html\\file.html", encoding="utf8"), "html.parser")【参考方案2】:

你可以试试:

r = urllib.urlopen('http://www.elnorte.ec/')
x = BeautifulSoup.BeautifulSoup(r.read)
r.close()

print x.prettify('latin-1')

我得到了正确的输出。 哦,在这种特殊情况下你也可以x.__str__(encoding='latin1')

我猜这是因为内容采用 ISO-8859-1(5) 格式,并且元 http-equiv 内容类型错误地显示为“UTF-8”。

你能确认一下吗?

【讨论】:

你好外国人,谢谢你的回答。你说得对,如果我用 'latin-1' 参数美化它,我会得到所有正确的重音和所有字符串。但是,我需要通过汤来处理链接,如果我再次尝试用字符串制作汤,它会再次混淆重音。 其实,没关系,现在我在尝试您的建议时遇到错误:UnicodeEncodeError: 'latin-1' codec can't encode characters in position 62-63: ordinal not in range(256 ) 如果我这样做,它似乎又可以工作了:x = BeautifulSoup.BeautifulSoup(r.read(), fromEncoding='latin-1'),但同样,如果我尝试制作新汤美化的字符串,它又搞砸了:/ 终于明白了,只需要:soup = BeautifulSoup(content, fromEncoding='latin-1') 然后在解析链接时:i_title = item.contents[0].encode ('latin-1').decode('utf-8') 似乎可以解决问题。感谢您的帮助:) 代码似乎有误(double BeatifulSoup?):AttributeError: type object 'BeautifulSoup' has no attribute 'BeautifulSoup' - 接口可能改变了?【参考方案3】:

你可以试试这个,它适用于每种编码

from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
headers = "User-Agent": USERAGENT
resp = requests.get(url, headers=headers)
http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None
html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True)
encoding = html_encoding or http_encoding
soup = BeautifulSoup(resp.content, 'lxml', from_encoding=encoding)

【讨论】:

不错的答案,但我会放弃headers(不是真的需要,因为你没有定义USERAGENT,所以不能盲目复制粘贴代码)。【参考方案4】:

我建议采取更有条理的万无一失的方法。

# 1. get the raw data 
raw = urllib.urlopen('http://www.elnorte.ec/').read()

# 2. detect the encoding and convert to unicode 
content = toUnicode(raw)    # see my caricature for toUnicode below

# 3. pass unicode to beautiful soup. 
soup = BeautifulSoup(content)


def toUnicode(s):
    if type(s) is unicode:
        return s
    elif type(s) is str:
        d = chardet.detect(s)
        (cs, conf) = (d['encoding'], d['confidence'])
        if conf > 0.80:
            try:
                return s.decode( cs, errors = 'replace' )
            except Exception as ex:
                pass 
    # force and return only ascii subset
    return unicode(''.join( [ i if ord(i) < 128 else ' ' for i in s ]))

你可以推理,不管你扔什么,它总是会向 bs 发送有效的 unicode。

因此,每次您有新数据时,您的解析树都会表现得更好,并且不会以更新更有趣的方式失败。

试验和错误在代码中不起作用 - 组合太多:-)

【讨论】:

【参考方案5】:

第一个答案是对的,这个功能有时是有效的。

    def __if_number_get_string(number):
        converted_str = number
        if isinstance(number, int) or \
            isinstance(number, float):
                converted_str = str(number)
        return converted_str


    def get_unicode(strOrUnicode, encoding='utf-8'):
        strOrUnicode = __if_number_get_string(strOrUnicode)
        if isinstance(strOrUnicode, unicode):
            return strOrUnicode
        return unicode(strOrUnicode, encoding, errors='ignore')

    def get_string(strOrUnicode, encoding='utf-8'):
        strOrUnicode = __if_number_get_string(strOrUnicode)
        if isinstance(strOrUnicode, unicode):
            return strOrUnicode.encode(encoding)
        return strOrUnicode

【讨论】:

以上是关于Python和BeautifulSoup编码问题[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Python爬虫:BeautifulSoup库

python爬虫 BeautifulSoup

python学习之爬虫:BeautifulSoup

使用 beautifulSoup、Python 在 h3 和 div 标签中抓取文本

python3解析库BeautifulSoup4

BeautifulSoup4