web-dev-qa-db-ja.com

PythonおよびBeautifulSoupエンコーディングの問題

BeautifulSoupを使用してPythonを使用してクローラーを作成しています。このサイトに到達するまでは、すべてが泳ぎ続けていました。

http://www.elnorte.ec/

要求ライブラリでコンテンツを取得しています:

r = requests.get('http://www.elnorte.ec/')
content = r.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'、しかしまだサイコロはありません。

どんなポインターでも大歓迎です。

23
David

試してみてください:

_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 content-typeが誤って「UTF-8」を示しているためだと思います。

確認してもらえますか?

18
Gaikokujin Kun

あなたの場合、このページに間違ったutf-8データがあり、BeautifulSoupを混乱させ、あなたのページがwindows-1252を使用していると思わせる場合、このトリックを行うことができます:

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

これを行うことにより、ページソースから間違ったシンボルを破棄し、BeautifulSoupはエンコードを正しく推測します。

「無視」を「置換」に置き換え、「?」のテキストを確認できます。廃棄されたものを見るためのシンボル。

実際には、100%の確率で毎回ページエンコーディングを推測できるクローラーを記述するのは非常に難しいタスクです(現在、ブラウザは非常に優れています)。たとえば、「chardet」などのモジュールを使用できますが、たとえば、 ISO-8859-2として、これも正しくありません。

ユーザーが提供できるページのエンコーディングを取得する必要がある場合は、マルチレベル(utf-8を試す、latin1を試す、など)検出関数を作成する必要があります(プロジェクトで行ったように) )またはCモジュールとしてfirefoxまたはChromiumの検出コードを使用します。

23
Riz

これを試すことができます。これはすべてのエンコーディングで機能します

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)
5
Shawn

私は、より系統的な愚か者証拠アプローチをとることを提案します。

# 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に送信します。

その結果、新しいデータを取得するたびに、解析されたツリーの動作が改善され、より興味深い方法で失敗することがなくなります。

試行錯誤はコードでは機能しません-組み合わせが多すぎます:-)

2
vpathak

最初の答えは正しいです、この機能は時々効果的です。

    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
2
Tabares