: O. Yuanying

Pythonでスパムフィルタを書く/トークナイザの実装

仕様も適当に決まったことだし、 実装に入ってみる。

実装

トークン構成要素の正規表現の生成

Unicode.orgの Blocks.txtLineBreak.txt を利用して同一文字種を抜き出す正規表現を書くのだが、 手作業でやるのもめんどくさいし、 スクリプト書いて自動処理してもいいのだけど今回のテーマから逸脱する気もするので RAA - wakeru で生成された正規表現をそのまんまパクってくる。

ライセンスも一応 Ruby's と書いてあるので問題ないだろう!

ってことで pgspam.blocks モジュールは こんな感じ になる。

これはwakeruの make_block_src.rb を python用の正規表現モジュールを生成するように修正した ものを利用して生成した。

pgspam.blocks のテストはこんな感じ。


#!/usr/bin/env python
# encoding: utf-8
"""
blocksTest.py

Created by Yuanying on 2008-06-04.
Copyright (c) 2008 fraction.jp. All rights reserved.
"""
import sys, os
script_dir = os.path.dirname(os.path.abspath(__file__))
base_dir = script_dir + os.sep + '..'
if not base_dir in sys.path:
    sys.path.insert(0, base_dir)

import unittest
import pgspam.blocks

class BlocksTest(unittest.TestCase):
    def setUp(self):
        pass
    
    def test_alphabet_search(self):
        """test_alphabet"""
        m = pgspam.blocks.re_block.search(u' aaa wewe 21')
        assert m.group() == u'aaa'
    
    def test_alphabet_search2(self):
        """docstring for test_alphabet_match2"""
        m = pgspam.blocks.re_block.search(u'*&[aaa]')
        assert m.group() == u'aaa'

    def test_hiragana_search2(self):
        """docstring for test_hiragana_search2"""
        m = pgspam.blocks.re_block.search(u'*&[あああ]')
        assert m.group() == u"あああ"

    
if __name__ == '__main__':
    unittest.main()

実行してみる。

$ python pgspam-project/test/blocksTest.py 
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

ちゃんと日本語も抜き出してるね!すばらしい。

トークナイザの実装

生成した正規表現を使って連続する同一文字種をトークンとみなして、 文字列をトークナイズする関数を実装してみる。

Pythonの正規表現オブジェクトにfindallってメソッドがあるので、 そのまんまこれを使えば良いのかな?


import pgspam.blocks

def execute(words):
    """docstring for execute"""
    return pgspam.blocks.re_block.findall(words)

テストを実行してみる。

[yuanying@Magnus] ~/Projects/python
$ python pgspam-project/test/tokenizerTest.py
E.F..
======================================================================
ERROR: docstring for test_append_prefix_to_token
----------------------------------------------------------------------
Traceback (most recent call last):
  File "pgspam-project/test/tokenizerTest.py", line 36, in test_append_prefix_to_token
    rtn = pgspam.tokenizer.execute(u'/', u'url*')
TypeError: execute() takes exactly 1 argument (2 given)

======================================================================
FAIL: docstring for test_ignore_only_one_character
----------------------------------------------------------------------
Traceback (most recent call last):
  File "pgspam-project/test/tokenizerTest.py", line 32, in test_ignore_only_one_character
    assert [u'word', u'word4'] == rtn
AssertionError

----------------------------------------------------------------------
Ran 5 tests in 0.001s

FAILED (failures=1, errors=1)

ああ、、一文字は無視するっていう仕様とprefixをつけるって仕様を忘れてた。

正規表現にマッチしたトークンのチェックと修正を行うので、 findallではなくイテレータを使うように修正。

はじめてのイテレータだワクワク。


import pgspam.blocks

def execute(words, prefix=''):
    """docstring for execute"""
    rtn = []
    for m in pgspam.blocks.re_block.finditer(words):
        if (m.end() - m.start()) > 1:
            rtn.append(prefix + m.group(0))
    return rtn

こんな感じに修正。

リストの追加に append とか書くのがめんどくさい件。

[yuanying@Magnus] ~/Projects/python
$ python pgspam-project/test/tokenizerTest.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.001s

OK

テストを華麗にパス!(テストにもバグが混入してたことは気にしない。)

ここまでのプロジェクトのソースコード