【问题标题】:Python and BeautifulSoup encoding issues [duplicate]Python和BeautifulSoup编码问题[重复]
【发布时间】: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',但还是没有骰子。

任何指针将不胜感激。

【问题讨论】:

    标签: python unicode utf-8 beautifulsoup


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 2011-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多