Webstemmer使おうとして文字コードに悩まされたけど解決
昨日の記事で書いた、Webstemmerステップ4の文字コードエラーが治った。
昨日の状況
『ステップ4. 学習したパターンを使って本文を抽出する』を実行すると文字化け
C:\Python27\Lib\site-packages\webstemmer>extract.py -C euc-jp asahi.pat asahi.201107111623.zip PATTERN: 201107111602/www.asahi.com/digital/av/TKY201106280357.html MAIN-6: 召ツ宵エ。・将ツ。・。・娼。将苡ェイ召エ宵ョ将」召ェツ。ェ。。ェゥ。ェメ。ェ。\将「\宵ェ\召エ召ツ娼ャ。ェ。。
- 元の文字列はUnicode文字列
- EUC-JP,shift-jis,utf-8などひと通りエンコード試したけど全て文字化け
- PyhtonはUnicode文字列を直接表示できない
- 直接表示しようとすると"UnicodeEncodeError: 'cp932' codec can't encode character u'\xa1' in position 0:illegal multibyte sequence"
- try-except文を使って1文字ずつ↑のエラー吐かせてみた
- u'\xa1' u'\xca' u'\xa3' u'\xb6' u'\xb7' u'\xee' u'\xa3' u'\xb2' ...
- Unicode対応 文字コード表でEUCの文字コードを2バイトずつ確認すると、"a1ca,a3b6,b7ee,a3b2"→"(,6,月,2"
- 本文と一致
新たに分かったこと
- 元の文字列はEUC-JPっぽい "\xa1\xca\xa3\xb6\xb7\xee\xa3\xb2" = "(6月2"
- extract.pyは文字コード自動認識しない
- オプション-cでデフォルトの文字コードを指定する
とりあえず、文字コード周りをEUC-JPに統一してみることにした。
↓元のコードのエンコードを外して
def dump_text(self, name, tree, pat_threshold, diffscore_threshold, main_threshold, codec_out='utf-8', strict=True): #enc = lambda x: x.encode(codec_out, 'replace') enc = lambda x: x ........ if pat1.title_sectno < sectno and main_threshold <= sect.mainscore: for b in sect.blocks: print 'MAIN-%d: %s' % (sect.id, enc(b.orig_text)) else: for b in sect.blocks: print 'SUB-%d: %s' % (sect.id, enc(b.orig_text)) print return
オプションに-c euc-jpを追加して
C:\Python27\Lib\site-packages\webstemmer>extract.py -c euc-jp asahi.pat asahi.201107111623.zip
実行すると
... !MATCHED: 201107111623/www.asahi.com/digital/av/TKY201106280357.html PATTERN: 201107111602/www.asahi.com/digital/av/TKY201106280357.html MAIN-6: (6月29日発売のAERAムック『AERA×Apple アップルはお好き ですか』から抜粋した記事です) MAIN-6: 死者3025人、行方不明者2770人、全壊家屋2万8千戸……(6月10日現 在)。3月11日に起きた東日本大震災でもっとも被害の大きかった自治体のひとつ、宮城 県石巻市。海に近い市街地は石巻市立病院などいくつかの建物を除き、街は跡形もなく流 された。 ...
治ったー!ようやく先に進めます。
色々分からない所もあるけど今は放置。余裕ができたら細かいところも追って行きたい。
Webstemmer使おうとして引数エラーと文字コードに悩まされる
Webページの本文を抽出したくて、MOONGIFTで見つけたWebstemmerを使おうと思ったら、いろんな障害にぶつかった。Python歴3日なので、ソース読むのも一苦労。
エラー1
『ステップ1. 学習するためのHTMLページを取得する』をやろうとしたら早速エラーが。
C:\Python27\Lib\site-packages\webstemmer> textcrawler.py -o asahi http://asahi.com/ Writing: 'asahi.201107111850.zip' Traceback (most recent call last): File "C:\Python27\Lib\site-packages\webstemmer\textcrawler.py", line 444, in <module> if __name__ == '__main__': main() File "C:\Python27\Lib\site-packages\webstemmer\textcrawler.py", line 436, in main debug=debug).run() File "C:\Python27\Lib\site-packages\webstemmer\textcrawler.py", line 356, in __init__ cookie_file, acldb, urldb, default_charset, delay, timeout, debug) File "C:\Python27\Lib\site-packages\webstemmer\textcrawler.py", line 113, in __init__ self.robotstxt.read() File "C:\Python27\lib\robotparser.py", line 57, in read f = opener.open(self.url) File "C:\Python27\lib\urllib.py", line 205, in open return getattr(self, name)(url) File "C:\Python27\lib\urllib.py", line 342, in open_http h.endheaders(data) TypeError: endheaders() takes exactly 1 argument (2 given)
エラーの意味は「引数2つあるよ!1つにしてよ!」らしい。(参考:『初めてのPython』 IV部 演習問題 - ケーズメモ)
試しにendheaders()の元であるhttplib.pyの中身を覗くと、
class HTTPConnection: ... def endheaders(self, message_body=None): ...
となってる。引数がちゃんと2つあるのでこれはおかしい。
試行錯誤の結果、httplib.pyがそこかしこのフォルダに存在していることがわかったため、まずimportの優先順位を確認する。
import sys
sys.path
結果がこう。
['C:\\Python27\\Lib\\site-packages\\webstemmer', 'C:\\Python27\\Lib\\site-packages', 'C:\\Windows\\system32\\python27.zip', 'C:\\Python27\\DLLs', 'C:\\Python27\\lib', 'C:\\Python27\\lib\\plat-win', 'C:\\Python27\\lib\\lib-tk', 'C:\\Python27']
で、C:\Python27\Lib\site-packages\webstemmerの中を見ると、しっかりhttplib.pyが入ってる。こっちのendheaders()を確認すると、
class HTTPConnection: ... def endheaders(self): ...
引数が1つしかない。ようやく原因特定。
呼び出し先の引数を増やしても、呼び出し元の引数を減らしても一応動いたけど、引数減らすのはちょっと怖いので、webstemmerフォルダのC:\Python27\lib\httplib.pyに書き換えた。よく分からないしこれ以上触りたくない。
エラー2
『ステップ4. 学習したパターンを使って本文を抽出する』を実行し、やっと終わったと思いきや。
C:\Python27\Lib\site-packages\webstemmer>extract.py -C euc-jp asahi.pat asahi.201107111623.zip PATTERN: 201107111602/www.asahi.com/digital/av/TKY201106280357.html MAIN-6: 召ツ宵エ。・将ツ。・。・娼。将苡ェイ召エ宵ョ将」召ェツ。ェ。。ェゥ。ェメ。ェ。\将「\宵ェ\召エ召ツ娼ャ。ェ。。ェゥ。ェメ。ェ。召ツ娼ホ。ェ。。ゥテ。ゥテ。ォタ。ォゥ \。宵ェ\。゚\将ウ召ェチ召「・\召・召ェョ召・召・召ツ宵繽「・召ォア宵イ。ュ召ト将イ召・召「ト???将モ召ェョ召・召ツ宵ウ
文字化け!これは辛い。文字コード周りについて一から調べることになってしまった。
色々調べて分かったことは、
- 元の文字列はUnicode文字列
- EUC-JP,shift-jis,utf-8などひと通りエンコード試したけど全て文字化け
- PyhtonはUnicode文字列を直接表示できない
- 直接表示しようとすると"UnicodeEncodeError: 'cp932' codec can't encode character u'\xa1' in position 0:illegal multibyte sequence"
- try-except文を使って1文字ずつ↑のエラー吐かせてみた
- u'\xa1' u'\xca' u'\xa3' u'\xb6' u'\xb7' u'\xee' u'\xa3' u'\xb2' ...
- Unicode対応 文字コード表でEUCの文字コードを2バイトずつ確認すると、"a1ca,a3b6,b7ee,a3b2"→"(,6,月,2"
- 元記事の本文始めは「(6月29日発売のAERAムック『AERA×Apple アップルはお好きですか』から抜粋した記事です)」
- 合ってるし!
ここまでやって今日は諦めた。文字コード自体はeuc-jpで合ってるっぽいので、文字列の扱い方が良くないんだろうか?なんとか明日中に解決したい。