2011年4月10日日曜日

Beautiful Soupは、あなたにUnicodeを渡す、Dammit

あなたのドキュメントは、解析される時までにUnicodeに変換されます。Beautiful Soupは自身のデータ構造中にUnicode文字列だけを保存します。
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup("Hello")
soup.contents[0]
# u'Hello'
soup.originalEncoding
# 'ascii'

ここに、UTF-8でエンコードされた日本語ドキュメントを用いた例を示します:
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup("\xe3\x81\x93\xe3\x82\x8c\xe3\x81\xaf")
soup.contents[0]
# u'\u3053\u308c\u306f'
soup.originalEncoding
# 'utf-8'
str(soup)
# '\xe3\x81\x93\xe3\x82\x8c\xe3\x81\xaf'
# 注記: このビットはEUC-JPを使用します、そのため、cjkcodecsをインストールしているか、
# Python 2.4を使用している場合のみ動作します。
soup.__str__('euc-jp')
# '\xa4\xb3\xa4\xec\xa4\xcf'

Beautiful Soup は、あなたがBeautiful Soupに渡したドキュメントのエンコーディングを判定し、Unicodeに変換するために、UnicodeDammit と呼ばれるクラスを使います
もし、(ドキュメントの解析に Beautiful Soup を使わないで)他のドキュメントでエンコーディングの判定とUnicodeへの変換を行う必要がある場合、UnicodeDammit単独で使うことも出来ます。
 
UnicodeDammitは、Universal Feed Parser由来のコードにかなり依拠しています。
Pythonのバージョン2.4よりも古いものを使っている時は、より多くのコーデック、特にCJKコーデックをサポート出来るために、必ず cjkcodecsiconvcodec をダウンロードし、インストールしてください。
また、 より良い自動判定を行うには、chardet ライブラリをインストールしてください。

Beautiful Soupは、優先度の高い順に、以下のエンコーディングを試します。
ドキュメントをUnicodeに変換するには:

  • スープコンストラクタに、fromEncoding 引数として渡したエンコーディング
  • ドキュメント中で見つかったエンコーディング: 例えば、XML 宣言や(HTML ドキュメントでは) http-equiv META タグ。もし、Beautiful Soup がドキュメント中にこの種のエンコーディングを見つけたら、Beautiful Soup は再度、先頭からドキュメントを解析し、新しいエンコード化を試します。もし、あなたがエンコーディングを明白に指定し、それが正しく動作した場合だけが 唯一の例外です:この場合、ドキュメント中に見つけるあらゆるエンコーディングを無視します。
  • ファイル先頭の数バイトを見ることで、探ったエンコーディング。
    もし、この段階でエンコーディングが判定されたら、UTF-*か、EBCEICか、ASCIIのうちのいずれかでしょう。
  • chardetライブラリインストールしていた場合、同ライブラリによって探ったエンコーディング
  • UTF-8
  • Windows-1252

Beautiful Soup は、推測出来る場合は、ほとんどいつも正しく推測します。
しかし、宣言がなく、エンコーディングがおかしいドキュメントの場合は、しばしば推測することが出来ません。
エンコーディングは、Windows-1252になりますが、これはおそらく間違っています。
ここで、Beautiful Soupがエンコーディングを間違って推測する、EUC-JP の例を示します。
(再掲:この例はEUC-JPを使っているので、 Python 2.4を使用しているか、cjkcodecsをインストールしている場合のみ動作します。)
from BeautifulSoup import BeautifulSoup
euc_jp = '\xa4\xb3\xa4\xec\xa4\xcf'
soup = BeautifulSoup(euc_jp)
soup.originalEncoding # 'windows-1252'
str(soup) # '\xc2\xa4\xc2\xb3\xc2\xa4\xc3\xac\xc2\xa4\xc3\x8f'     # 間違い!

しかし、もし、あなたが fromEncodingを伴ってエンコーディングを指定したら、Beautiful Soupはドキュメントを正確に解析し、UTF-8に変換したり、EUC-JPに戻すことができます。
soup = BeautifulSoup(euc_jp, fromEncoding="euc-jp")
soup.originalEncoding
# 'windows-1252'

str(soup)
# '\xe3\x81\x93\xe3\x82\x8c\xe3\x81\xaf'                 # 正しい!

soup.__str__(self, 'euc-jp') == euc_jp
# True

もし、Windows-1252エンコーディング(やISO-8859-1、ISO-8859-2など、同様のエン コーディング)のドキュメントをBeautiful Soupに渡した場合、Beautiful SoupはスマートクォートとWindows特殊文字を見つけ、破壊するでしょう。
Beautiful Soupはそれらの文字をUnicodeの対応する文字に変換するのではなく、むしろ、それらを(BeautifulSoupだと)HTMLエンティティや(BeautifulStoneSoupだと)XMLエンティティに変換します。
これを回避するためには、スープコンストラクターにsmartQuotesTo=None を渡すことも出来ます:そして、スマートクォートは他のあらゆるネイティブエンコーディング文字のように、Unicodeに変換されるでしょう。
また、 BeautifulSoupBeautifulStoneSoupのデフォルトの動作を変更するために、 smartQuotesToに"xml" か "html"を渡すことが出来ます。
from BeautifulSoup import BeautifulSoup, BeautifulStoneSoup
text = "Deploy the \x91SMART QUOTES\x92!"

str(BeautifulSoup(text))
# 'Deploy the ‘SMART QUOTES’!'

str(BeautifulStoneSoup(text))
# 'Deploy the ‘SMART QUOTES’!'

str(BeautifulSoup(text, smartQuotesTo="xml"))
# 'Deploy the ‘SMART QUOTES’!'

BeautifulSoup(text, smartQuotesTo=None).contents[0]
# u'Deploy the \u2018SMART QUOTES\u2019!'

0 件のコメント:

コメントを投稿

【和訳】Django Rest Framework 目次

目次 【和訳】Django Rest Framework クイックスタート 【和訳】Django Rest Framework チュートリアル1:シリアル化 【和訳】Django Rest Framework チュートリアル2:リクエストとレスポンス 【和訳】Django Res...