web-dev-qa-db-ja.com

Pythonウェブサイトの正しいエンコーディング(Beautiful Soup))

HTMLページをロードしてテキストを出力しようとしていますが、Webページを正しく取得しているにもかかわらず、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である必要があります

10
user1767754

2つの間違いを犯しています。エンコーディングを誤って処理し、結果リストを、情報を失うことなく文字列に安全に変換できるものとして扱います。

まず、_response.text_は使用しないでください。ここでの問題はBeautifulSoupではありません。あなたは Mojibake を再エンコードしています。 requestsライブラリは、サーバーが明示的にエンコードを指定していない場合、デフォルトで_text/*_コンテンツタイプのLatin-1エンコーディングに設定されます。これは、HTTP標準でデフォルトとされているためです。

EncodingAdvancedのドキュメントのセクション を参照してください:

リクエストがこれを行わないのは、HTTPヘッダーに明示的な文字セットが存在しない場合のみですand _Content-Type_ヘッダーにtextが含まれています。 この状況では、RFC 2616は、デフォルトの文字セットが_ISO-8859-1_でなければならないことを指定しています。この場合、要求は仕様に従います。別のエンコーディングが必要な場合は、_Response.encoding_プロパティを手動で設定するか、生の_Response.content_を使用できます。

大胆な強調鉱山。

代わりに_response.content_生データを渡します。

_soup = BeautifulSoup(r.content)
_

BeautifulSoup 3を使用しているようですが、代わりにBeautifulSoup 4にアップグレードしたいと考えています。バージョン3は2012年に廃止され、いくつかのバグが含まれています。 _beautifulsoup4_プロジェクト をインストールし、_from bs4 import BeautifulSoup_を使用します。

BeautifulSoup 4は通常、解析時にHTML _<meta>_タグまたは提供されたバイトの統計分析のいずれかから、使用する適切なエンコーディングを理解するという優れた機能を果たします。サーバーが文字セットを提供する場合でも、これを応答からBeautifulSoupに渡すことができますが、requestsがデフォルトを使用しているかどうかを最初にテストします。

_encoding = r.encoding if 'charset' in r.headers.get('content-type', '').lower() else None
soup = BeautifulSoup(r.content, from_encoding=encoding)
_

最後に、BeautifulSoup 4では、soup.get_text()を使用してページからすべてのテキストを抽出できます。

_text = soup.get_text()
print text
_

代わりに、結果リストsoup.findAll()の戻り値)を文字列に変換しています。 Pythonのコンテナーはリストの各要素でrepr()を使用してデバッグ文字列を生成するため、これは機能しません。印刷できないもののエスケープシーケンスを取得するASCII文字。

34
Martijn Pieters

BeautifulSoupのせいではありません。これは、BeautifulSoupを使用する前にencodedTextを印刷することで確認できます。非ASCII文字はすでに意味不明です。

ここでの問題は、バイトと文字を混同していることです。違いの概要については、 Joelの記事の1つ を参照してください。ただし、要点は、バイトはバイト(追加の意味を持たない8ビットのグループ)であり、文字はテキストの文字列を構成します。 エンコードすると文字がバイトになり、デコードするとバイトが文字に戻ります

requestsドキュメント を見ると、r.textがバイトではなく文字で構成されていることがわかります。エンコードするべきではありません。そうしようとすると、バイト文字列が作成され、それを文字として処理しようとすると、悪いことが起こります。

これを回避するには2つの方法があります。

  1. r.contentに格納されている、デコードされていない生のバイトを使用します Martijnの提案どおり 。次に、自分でデコードして文字に変換できます。
  2. requestsでデコードを行いますが、正しいコーデックを使用していることを確認してください。この場合はUTF-8であることがわかっているので、r.encoding = 'utf-8'を設定できます。これをbeforeする前に(---)r.textにアクセスすると、r.textにアクセスすると、正しくデコードされ、文字列が得られます。文字エンコーディングをいじる必要はまったくありません。

ちなみに、Python 3を使用すると、文字列とバイト文字列の違いを維持しやすくなります。これは、さまざまな種類のオブジェクトを使用して表現する必要があるためです。

6
David Z

コードにいくつかのエラーがあります:

  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)]
    
1
lesingerouge