000000 ランダム
 ホーム | 日記 | プロフィール 【ログイン】

傀儡師の館.Python

PR

日記/記事の投稿

カレンダー

キーワードサーチ

▼キーワード検索

カテゴリ

バックナンバー

2017年09月
2017年08月
2017年07月
2017年06月
2017年05月
2017年04月
2017年03月
2017年02月
2017年01月
2016年12月

フリーページ

楽天プロフィール


kugutsushiさん

怠惰な人

フォローする


■ 趣味・関心キーワード
デジモノ 

サイド自由欄

設定されていません。
2008年05月25日
XML
カテゴリ:Python
Python でエンコーディングの自動判定をするにはいくつかの方法がある。文字列のコードを直接調べてその情報だけで判定するタイプと、HTML や XML ファイルに含まれるメタ情報なども利用し、そうした情報で判定できないものは文字コードの情報から判定するタイプがある。

前者のタイプでは、pykf や、nkfpython、kanjilib などがある。pykf は、ShiftJIS, EUC-JP, JISコードを相互に変換するためのPython拡張モジュールで、Universal Encoding Detector や encutils のようには、メタ情報を使わない直接文字コードをチェックするタイプ。日本語のみを前提とするのであれば、比較的短い文字列であっても、正しい判定をしてくれる。一時、入手が難しかったが、sourceforge で再公開されたので、今は入手できる。Windows 環境用には Python 2.4 と 2.5 のバイナリも公開されている (pykf download)。

nkfバージョン2用 python インターフェース という、日本語漢字フィルタ nkf バージョン2用の python インターフェースを公開されていらっしゃる方もあるが、バイナリは Python 2.4 用のみで、2007年2月1日が最終公開日。自分でコンパイルするには、nkf2 のソースコードも用意する必要がある。

Pure Python のものでは kanjilib などが is_sjis や is_euc といった関数を持っているので、これもあり(ちょっと古いソースなので、そのままだと warning が出る。ソースに EUC-JP が含まれているので、今時の環境で使うなら # -*- encoding: euc-jp -*- を先頭に入れる)。

後者のものとしては、Mark Pilgrim さんの Universal Encoding Detector があるが、他にも、Christof Hoeke さんの encutils があるのでちょっと使ってみた。Universal Encoding Detector も、encutils も、直接短い文字列を渡して判定すると、判定をしくじる場合があるが、HTML ファイルや XML ファイルのようにエンコーディングの指定がちゃんと含まれているようなものであれば問題はない。けれど、どちらもデフォルトが windows-1252 に落ちるようで、短めの文字列のみを判定させると、そこが不幸の原因になる場合がある。


Universal Encoding Detector



Universal Feed ParserUniversal Encoding Detector (Character Encoding Detection) を使う方法を見てみる。auto-detection code in Mozilla のアルゴリズムが使われていて、そこそこよい判定をしてくれる。

easy_install chardet でインストールして、

>>> import urllib
>>> urlread = lambda url: urllib.urlopen(url).read()
>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{'encoding': 'GB2312', 'confidence': 0.99}

ただし、エンコーディングが指定されている HTML ファイルや XML ファイル等であれば、メタ情報を利用しているので正しく判定されるので問題ないが、文字コード判定ライブラリ Universal Encoding Detector にあるように、短い個別の文字列を渡してやると判定をしくじる場合がある。

>>> import chardet
>>> chardet.detect("これはなに")
{'confidence': 0.5, 'encoding': 'windows-1252'}
>>> chardet.detect("蛇のとぐろを見る")
{'confidence': 0.21941992101760521, 'encoding': 'IBM866'}
>>> chardet.detect("東京のこれは何")
{'confidence': 0.5, 'encoding': 'windows-1252'}
>>> chardet.detect("文字列の判定をするのだ")
{'confidence': 0.5, 'encoding': 'windows-1252'}


句読点が含まれない短い文字列を判定させると、こける可能性があります。句読点を含む文字列なら大丈夫だと思いますが。

chardet.detect("。")
chardet.detect("、")

メタ情報がないときには文字の出現頻度を使っているからのようだ。

import pykf
pykf.guess("これはなに")
だとこけないので、短い文字列で日本語の文字列と分かっているなら pykf 等使った方が安全かもしれません。

ということで、そういう場合には pykf の方が安全と。

encutils - encoding detection collection for Python



encutils は、新しくエンコーディングの判定のために作られたライブラリで、HTML, XHTML, XML, CSS 等のテキストファイルのエンコーディングの判定ができる。Universal Encoding Detector を使うときには urllib を別途インポートしなければいけないのに対して、encutils は、内部で urllib をインポートしているので、少しだけ、コードの量が少なくて済む。また、media type の情報も取り出して利用できるようにしているところが特徴か。BOM 付きのファイルに対してもちゃんと見ているので安心。ただし、chardet と違って、判定の確信度はない。

easy_install encutils でインストールして試す。

>>> import encutils
>>> info = encutils.getEncodingInfo(url='http://cthedot.de/encutils/')
>>> info
<encutils.EncodingInfo object encoding='utf-8' mismatch=False at 0x12e3270>
>>> print info
utf-8
>>> print info.logtext
HTTP media_type: text/html
HTTP encoding: utf-8
HTML META media_type: text/html
HTML META encoding: utf-8
Encoding (probably): utf-8 (Mismatch: False)

Universal Encoding Detector にやったのと同じように意地悪してみる。

>>> encutils.tryEncodings('これはなに')
'windows-1252'
>>> encutils.tryEncodings('蛇のとぐろを見る')
'IBM866'
>>> encutils.tryEncodings('東京のこれは何')
'windows-1252'
>>> encutils.tryEncodings('文字列の判定をするのだ')
'windows-1252'
>>> encutils.tryEncodings('。')
'SHIFT_JIS'
>>> encutils.tryEncodings('、')
'SHIFT_JIS'

やはり、判定に失敗する。同じようなロジックなのだろうかと見てみると、encutils は tryEncodings の中で文字列を直接判定するときに、Universal Encoding Detector (chardet) がインストールされていると、それを使って判定していた。インストールされていなければ、独自のやり方で判定する。

try:
import chardet
encoding = chardet.detect(text)["encoding"]

独自の判定方法は、'ascii','iso-8859-1', 'windows-1252', 'utf-8' の順に、 text.encode(e) を try して、例外が発生したら次のエンコーディングを試し、変換できたら、それに決まりという流れ。従って、SHIFT_JIS や EUC-JP、ISO-2022-JP などに落ちることはない。ここに、'shift_jis', 'euc_jp', 'iso-2022-jp' を追加してやれば、chardet がなくても日本語が判定できるようになるだろう。けど、ちょっと強引だから精度は落ちるだろう。

なので、ここに pykf を使った判定を入れるようにすれば、高い精度の文字列判定ができるようになるはず。つまり、外側の情報を使って判定できるものはそうして、文字列を見なきゃ判定できないものは、pykf を使うという流れになる。

pykf + encutils



pykf は、ShiftJIS, EUC-JP, JISコードを相互に変換するためのPython拡張モジュールで、日本人の atsuo ishimoto さんが公開しているもの。エンコーディングの判定機能も持っている。オリジナルは kf -- 漢字コード変換ソフト で、これを Python 対応にしたものだが、pykf は UTF-8 の判定もできる。

import pykf

pykf.setdefault(pykf.SJIS) # デフォルトは SJIS にしてみる
pykf.setstrict(TRUE) # 厳しく判定

enc = {pykf.UNKNOWN:None, pykf.ASCII:'ASCII',
pykf.SJIS:'SHIFT-JIS', pykf.EUC:'EUC-JP',
pykf.JIS:'ISO-2022-JP', pykf.UTF8:'UTF-8',
pykf.UTF16:'utf-16', pykf.UTF16_BE:'utf-16_be',
pykf.ERROR:None}

def detect_encode(text):
g = pykf.guess(text)
return enc[g]

print detect_encode('これはなに')
print detect_encode('蛇のとぐろを見る')
print detect_encode('東京のこれは何')
print detect_encode('文字列の判定をするのだ')

とすれば、Windows の SJIS の環境からだと SHIFT-JIS が返る。なので、とりあえずちょっと試しに 上記コードを encutils の __init__.py の中に適当に入れて(475行目から)、

try:
import pykf
pykf.setdefault(pykf.SJIS)
enc = {pykf.UNKNOWN:None, pykf.ASCII:'ASCII',
pykf.SJIS:'SHIFT-JIS', pykf.EUC:'EUC-JP',
pykf.JIS:'ISO-2022-JP', pykf.UTF8:'UTF-8',
pykf.UTF16:'utf-16', pykf.UTF16_BE:'utf-16_be',
pykf.ERROR:None}
encoding = enc[pykf.guess(text)]

とかしてみる。これで、短い文字列を判定させても、判定を間違えることはなくなった。けど、これはこれで ISO-8859-1 の文字列を SJIS に判定したりとかなっちゃうので困るときもある。文字列が長くても、短くても、日本語でも西欧の文字でも、もう少し判断がこけない方法はないかなぁ。chardet のモデルを少し調整して、もう少し日本語に倒れるようにできればよいのだけど。あちらをたてれば、こちらがたたずの部分はあるだろうけど。。。。


なかのひと






最終更新日  2008年05月25日 23時11分30秒
コメント(1) | コメントを書く
[Python] カテゴリの最新記事

Copyright (c) 1997-2017 Rakuten, Inc. All Rights Reserved.