@blog.justoneplanet.info

日々勉強

MeCabをインストールする

■インストール

以下のコマンドでインストールできる。

wget http://sourceforge.net/projects/mecab/files/mecab/0.98/mecab-0.98.tar.gz/download
tar xvzf mecab-0.98.tar.gz
cd mecab-0.98
./configure
make
make install

辞書

utf-8で使用したいので以下のconfigureオプションを使用する。

wget http://sourceforge.net/projects/mecab/files/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz/download
tar xvzf mecab-ipadic-2.7.0-20070801.tar.gz
cd mecab-ipadic-2.7.0-20070801
./configure --with-charset=utf8
make
make install

libiconvが必要になる。

■実行

以下のようにして使用する。

mecab
にわにはにわにわとりがいる
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
わに	名詞,一般,*,*,*,*,わに,ワニ,ワニ
はにわ	名詞,一般,*,*,*,*,はにわ,ハニワ,ハニワ
にわとり	名詞,一般,*,*,*,*,にわとり,ニワトリ,ニワトリ
が	助詞,格助詞,一般,*,*,*,が,ガ,ガ
いる	動詞,自立,*,*,一段,基本形,いる,イル,イル
EOS

ちょっと意地悪すぎたので入力を漢字にする。

庭には二羽鶏がいる
庭	名詞,一般,*,*,*,*,庭,ニワ,ニワ
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
二	名詞,数,*,*,*,*,二,ニ,ニ
羽	名詞,接尾,助数詞,*,*,*,羽,ワ,ワ
鶏	名詞,一般,*,*,*,*,鶏,ニワトリ,ニワトリ
が	助詞,格助詞,一般,*,*,*,が,ガ,ガ
いる	動詞,自立,*,*,一段,基本形,いる,イル,イル
EOS

正しく分類できた。

■Pythonから実行

PHP用のライブラリはあるようだが、久しぶりのPythonで楽しむことにする。

python-devel

セットアップスクリプトを実行するのに必要になる。

yum insatll python-devel

mecab-python

以下のコマンドでインストールできる。

wget http://sourceforge.net/projects/mecab/files/mecab-python/0.98/mecab-python-0.98.tar.gz/download
tar xvzf mecab-python-0.98.tar.gz
cd mecab-python-0.98
python setup.py build
python setup.py install

テストスクリプトの文字コードを指定する。

vi test.py

2行目に以下のコードを加える。

# -*- coding: utf-8 -*-

以下のコマンドで実行する。

python test.py

■参考

スクリプト言語のバインディング

pywebsocketをインストールする

■pywebsocket単体で動作させる

draft75

vi /home/pywebsocket-0.5.2/src/mod_pywebsocket/standalone.py

以下の部分を

parser.add_option('--allow-draft75', dest='allow_draft75',
                      action='store_true', default=False,
                      help='Allow draft 75 handshake')

以下のように変更する。

parser.add_option('--allow-draft75', dest='allow_draft75',
                      action='store_true', default=True,
                      help='Allow draft 75 handshake')

起動

python /home/pywebsocket-0.5.2/src/mod_pywebsocket/standalone.py -p 8800 -d /home/pywebsocket-0.5.2/src/example

WebSocketで8800ポートにアクセスする。(●´ω`●)

■apacheモジュールとして動作させる

http-devel

yum install http-devel

mod_python

wget http://archive.apache.org/dist/httpd/modpython/mod_python-3.3.1.tgz
tar xvzf mod_python-3.3.1.tgz
cd mod_python-3.3.1
./configure –with-apxs=/usr/sbin/apxs –with-python=/usr/bin/python
make
make install
設定
vi /etc/httpd/conf.d/python.conf

以下の記述の下に

LoadModule python_module modules/mod_python.so

以下の記述を付加する。

AddHandler mod_python .py

mod_pywebsocket

wget http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.5.2.tar.gz
tar xvzf mod_pywebsocket-0.5.2
cd pywebsocket-0.5.2/src
python setup.py build
python setup.py install
設定
vi /etc/httpd/conf.d/python_mod_pywebsocket.conf

以下を記述する。

<IfModule python_module>
    PythonPath "sys.path+['/usr/lib/python2.4/site-packages/']"
    PythonOption mod_pywebsocket.handler_root /var/www/html/wsh
    PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
    PythonOption mod_pywebsocket.allow_draft75 On
</IfModule>
/etc/init.d/httpd restart

起動

cp /home/pywebsocket-0.5.2/src/example/echo_wsh.py /var/www/html/wsh/

WebSocketクライアントで/echoにアクセスする。(●´ω`●)

■ベンチマーク

正確性は微妙だが100クライアントからの接続でのロードアベレージは、「node : pywebsoket(standalone) : pywebsoket(apache) = 0.15 : 0.25 : 0.8」となった。最初の2つの差は微妙だったが、Apacheモジュールとして動作させた時のリソースの消費量は明らかに大きいようだ。

DjangoでFormを作る

前回の記事のコードをベースに解説する。

■Formクラス

以下のようにFormクラスを継承して、ArticleSearchFormクラスを生成する。

# -*- coding: utf-8 -*-
from django import forms

class ArticleSearchForm(forms.Form):
    word = forms.CharField()

■ビュー関数

以下のようにsearch関数を定義する。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            articles = Article.objects.filter(title = form.cleaned_data['word'])
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

この場合、タイトルが完全一致しないと検索にヒットしないので以下のように__containsを付加する。これはSQLでいうLIKEのような役割だ。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            articles = Article.objects.filter(title__contains = form.cleaned_data['word'])
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

更に、条件をORで結合させたい場合は以下のようにする。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm
from django.db.models import Q

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            word     = form.cleaned_data['word']
            articles = Article.objects.filter(Q(title__contains = word) | Q(content__contains = word))
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

この辺はZendの方がSQLチックで直感的な気がする。

■テンプレート

以下のようにテンプレートを記述する。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>search</title>
<head>

<body>
<p>{{error_msg}}</p>
<form action="/search/" method="POST">
{% csrf_token %}
{{form.word}}
<input type="submit" value="search">
</form>
<br>
<table>
<tr>
<th>title</th>
<th>date</th>
</tr>
{% for article in articles %}
<tr>
<td>{{article.title}}</td>
<td>{{article.date}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

「setting.pyのMIDDLEWARE_CLASSESにdjango.middleware.csrf.CsrfViewMiddlewareが含まれている」かつ「ビュー関数でRequestContextを使用している」場合、{% csrf_token %}にCSRF対策用のトークンが埋め込まれる。

■URL設定

以下のように記述してURLを設定する。

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^article/(?P<id>[0-9]+)/$', 'dj.article.views.article'),
    (r'^search/$', 'dj.article.views.search'),
)

DjangoでHTTPレスポンスコードを変更する

前回のままだとURLのidが存在しなかった場合、500番を返すが本来404であって欲しい。以下のように記述することで実現できる。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader
from models import Article

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

Djangoでセッションを使う

ビュー関数の引数のrequestオブジェクトのsessionプロパティを使用する。ちなみにsessionを利用するには、MIDDLEWARE_CLASSESに「django.contrib.sessions.middleware.SessionMiddleware」が含まれている必要がある。

■取得

以下のようにしてセッションに格納した情報を取得することができる。

def get(request):
    data = request.session.get('key','')

第二引数はデフォルト値であり、省略することが可能である。

■保存

以下のようにしてセッションに値を保存することができる。

def set(request):
    request.session['key'] = [1, 2, 3]

デフォルトのセッション保存先はDBである。

Djangoで管理画面を作る

■設定ファイル編集

vi setting.py

以下のように記述する。

INSTALLED_APPS = (
    'dj.article',
    'django.contrib.admin',
)

以下のコマンドでDBを同期する。

./manage.py syncdb

■管理項目追加

以下のディレクトリにadmin.pyを追加する。

cd article
vi admin.py

admin.pyには以下のように記述する。

# -*- coding: utf-8 -*-
from django.contrib import admin
from dj.article.models import Article

admin.site.register(Article)

たったこれだけでarticleの追加ができるようになる。

■URL設定

vi urls.py

以下のように記述する。

from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^article/(?P<id>[0-9]+)/$', 'dj.article.views.article'),
)

以下のコマンドで開発サーバを起動してブラウザで/adminにアクセスする。

./manage.py runserver 192.168.1.123:8080

そう!管理画面はもうできてるんですな。

■つくり込む

dj/article/model.py

以下のように__unicode__メソッドを定義すると一覧の表示名がタイトルになる。

# -*- coding: utf-8 -*-
from django.db import models

# Create your models heire.
class Article(models.Model):
    id      = models.AutoField(primary_key = True)
    title   = models.CharField(max_length = 256)
    content = models.TextField()
    date    = models.DateField()

    def __unicode__(self):
        return self.title

    class Meta:
        db_table = 'article'

dj/article/admin.py

表示項目を増やしたい場合は以下のように記述する。

# -*- coding: utf-8 -*-
from django.contrib import admin
from dj.article.models import Article

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'date')# 表示項目

admin.site.register(Article, ArticleAdmin)

Pythonの条件節

■比較の連鎖

Pythonでは以下のように比較を連続して記述できる。

a = 3
b = 5
c = 7
if(a < b < c):
    print('hoge');
#hoge

ちなみにPHPではできない。

$a = 3;
$b = 5;
$c = 7;
if($a < $b < $c){
    print('hoge');
}
//Parse error: syntax error, unexpected '&lt;'

■条件節の中の代入文

Pythonでは以下のように条件節で代入文を記述することはできない。

b = [0, 3, 5]
if((a = b[1]) > 0):
    print('hoge')
#SyntaxError: invalid syntax

これについては公式マニュアルで以下のように説明されている。

Python では、C 言語と違って、式の内部で代入を行えないので注意してください。 C 言語のプログラマは不満を呈するかもしれませんが、この仕様は、 C 言語 プログラムで遭遇する、式の中で == のつもりで = とタイプ してしまうといったありふれた問題を回避します。

ちなみにPHPではできる。

$b = array(0, 3, 5);
if(($a = $b[1]) > 0){
    print('hoge');
}
#hoge

Djangoをインストールする

以下のコマンドでインストールする。

wget http://www.djangoproject.com/download/1.2.3/tarball/
tar xzvf Django-1.2.3.tar.gz
cd Django-1.2.3
python setup.py install

以下のコマンドでインストールできたか確認する。

python
import django
print(django.VERSION)
#(1, 2, 3, 'final', 0)

■プロジェクト作成

以下のコマンドでプロジェクトを作成する。

django-admin.py startproject dj

■開発サーバ起動

以下のコマンドで開発サーバを起動する。

cd dj
chmod 0744 manage.py
./manage.py runserver

以下のようにポートを指定することもできる。

cd dj
chmod 0744 manage.py
python ./manage.py runserver 0.0.0.0:80

■設定ファイル

以下のコマンドで設定ファイルを編集する。

vi setting.py

試しに編集してみる。

タイムゾーン

以下の項目を

TIME_ZONE = 'America/Chicago'

以下のように変更する。

TIME_ZONE = 'Asia/Tokyo'

言語

以下の項目を

LANGUAGE_CODE = 'en-us'

以下のように変更する。

LANGUAGE_CODE = 'ja'

データストア

以下のように記述して設定する。

DATABASES = {
    'default': {
        'ENGINE': 'sqlite3',
        'NAME': os.path.dirname(__file__) + os.sep + 'dj.sqlite',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}

MySQLならば以下のようになる。

DATABASES = {
    'default': {
        'ENGINE': 'mysql',
        'NAME': 'dbname',
        'USER': 'admin',
        'PASSWORD': 'hogehoge',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

■DBセットアップ

このまま初期状態を生成しようとするとエラーが出るはずだ。

sqlite

以下のようなエラーが表示される。

ImproperlyConfigured("Error loading %s: %s" % (module, exc))
django.core.exceptions.ImproperlyConfigured: Error loading pysqlite2 module: No module named pysqlite2

pythonからsqliteに接続するドライバが無いらしいので、以下のコマンドでインストールする。

wget http://pypi.python.org/packages/source/p/pysqlite/pysqlite-2.6.0.tar.gz
tar xzvf pysqlite-2.6.0.tar.gz
cd pysqlite-2.6.0
python setup.py install

MySQL

以下のようなエラーが表示される。

raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named MySQLdb

pythonからMySQLに接続するドライバが無いらしいので、以下のコマンドでインストールする。

setuptools

MySQL-pythonをインストールする際に必要なので、先にインストールする。

wget http://pypi.python.org/packages/source/s/setuptools/setuptools-0.6c11.tar.gz
tar xzvf setuptools-0.6c11.tar.gz
cd setuptools-0.6c11
python setup.py install
MySQL-python

以下のコマンドでMySQL-pythonをインストールする。

wget http://pypi.python.org/packages/source/M/MySQL-python/MySQL-python-1.2.3.tar.gz
tar xzvf MySQL-python-1.2.3.tar.gz
cd MySQL-python-1.2.3
python setup.py install

エラー1

インストールの途中で以下のようなエラーが発生するかもしれない。

_mysql.c:2444: error: '_mysql_ConnectionObject' has no member named 'open'
error: command 'gcc' failed with exit status 1
対策

以下のコマンドでmysql-develをインストールする。

yum --enablerepo=remi,epel install mysql-devel.x86_64

■アプリケーション生成

以下のコマンドでアプリケーション(の一部)を生成する。

./manage.py startapp article

以下のコマンドを実行してsetting.pyにも反映させる。

vi setting.py

以下の部分を

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
)

以下のように変更する。

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'dj.article',
)

■モデル

以下のコマンドでモデルを編集する。

cd article
vi model.py

編集前は以下のように記述されている。

from django.db import models

# Create your models here.

例えば以下のようにモデル部分を記述する。

# -*- coding: utf-8 -*-
from django.db import models

# Create your models here.
class Article(models.Model):
    id      = models.AutoField(primary_key = True)
    title   = models.CharField(max_length = 256)
    content = models.TextField()
    date    = models.DateField()
    class Meta:
        db_table = 'article'# table name

■コマンドラインから操作

なんでも生成したクラスを使ってデータの挿入ができるらしいぞ!

./manage.py shell

以下を入力する。

from article.models import Article
article = Article()
article.title = 'hogehoge'
article.content = 'this is a hoge pen'
article.date = '2010-10-10'
article.save()

なんと直感的なデータ挿入でござんしょう!

■テンプレートの生成

以下のコマンドを入力し設定ファイルを編集する。

vi setting.py

以下のように記述しテンプレートファイルの格納場所を指定する。

TEMPLATE_DIRS = (
    '/var/www/vhosts/domain/subdomains/dj/templates',
)

/var/www/vhosts/domain/subdomains/dj/templates/page/article.html

以下のようにテンプレートファイルを記述する。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{{article.title}}</title>
<head>

<body>
<h2>{{article.title}}</h2>
<nav class="date">{{article.date}}</nav>
<article>
{{article.content}}
</article>
</body>
</html>

分かると思うけど、{{}}の部分は変数が展開されるところですな。アサインした変数は自動でエスケープされるが、その処理を無効化したい場合は以下のように記述する。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{{article.title}}</title>
<head>

<body>
<h2>{{article.title}}</h2>
<nav class="date">{{article.date}}</nav>
<article>
{{article.content|safe}}
</article>
</body>
</html>

これでHTMLを含んだコンテンツを反映することができる。但し、XSSには注意すること。

■URLディスパッチャの設定

vi urls.py

以下のように記述する。

urlpatterns = patterns('',
    (r'^article/(?P<id>[0-9]+)/$', 'dj.article.views.article'),
)

■ビュー関数

以下のコマンドでビュー関数を付加する。

vi article/views.py

以下のように記述する。

from django.http import HttpResponse
from django.template import Context, loader
from models import Article

# Create your views here.
def article(request, id):
    article = Article.objects.get(id = id)
    tpl     = loader.get_template('page/article.html')
    context = Context({'article' : article})
    return HttpResponse(tpl.render(context))

もう直感的にガシガシくる感じだね!

■確認

以下のコマンドを入力して開発サーバを起動しブラウザで確認する。

./manage.py runserver 192.168.1.123:8080

Pythonで文字列の変数を展開する

以下のようにして文字列内の変数を展開する。

a = 'This'
b = 'pen'
print "%(a)s is a %(b)s" % locals()
#This is a pen

以下のように書いたほうが分かりやすい気がする。

print "%(a)s is a %(b)s" % {"a" : "This", "b" : "pen"}
#This is a pen

Pythonでは本来出来ないものなので、この辺はPHPの方が気持ちいいね。

Pythonでモジュールを使う

モジュールは Python の定義や文が入ったファイル。 ファイル名はモジュール名に接尾語 ,py がついたものになる。 モジュールの中では、モジュール名をグローバル変数 __name__ で取得できる。

■クラスのモジュール

/dog.py

まずはモジュールを定義する。

class Dog:
    name = ''
    def __init__(self, name):
        self.name = name
    def cry(self):
        print(self.name)

モジュールだからといって特に変わった記述をする必要はない。

/index.py

以下のようにして上述のモジュールを使用する事ができる。

import dog

pochi = dog.Dog('pochi')
pochi.cry()

ちなみに以下のようにしてしまうと、Dogクラスにアクセスできなくなってしまうので注意が必要だ。

import dog

dog = 'pochi'
pochi = dog.Dog('pochi')
pochi.cry()

また、以下のようにしても使用する事ができる。この場合、モジュール名dogがローカルのシンボルテーブル内では定義されない。

from dog import Dog

pochi = Dog('pochi')
pochi.cry()

更に、以下のように*を使用することもできる。この場合、アンダーバーで始まる名前以外を全てインポートすることができる。

from dog import *

pochi = Dog('pochi')
pochi.cry()

■関数のモジュール

/hoge.py

モジュールファイルに関数が記述されていることもあるはずだ。

str = 'fuga'
def fuga():
    return str

/index.py

以下のようにしてhogeモジュールを使用することができる。また、モジュール内のグローバル変数にアクセスすることもできる。

import hoge

hoge.str = 'piyo'
value = hoge.fuga()
print(value)# piyo

■モジュール検索パス

以下のようにインポートする場合を考える。

import hoge

上述の場合、モジュールは以下のパスから検索される。

  • カレントディレクトリ内のhoge.py
  • sys.pathリストのディレクトリ内のhoge.py

モジュールを定義するときは標準モジュールと名前が被らないようにしないと!

■パッケージ

/Hoge/__init__.py

パッケージの各ディレクトリに必要なファイル。ファイルの中身は空で良い。

/Hoge/dog.py

パッケージ内のモジュールについては以下のようにする。特に変わったところはない。

class Dog:
    __name = ''
    def __init__(self, name):
        self.__name = name
    def cry(self):
        print(self.__name + ', bow!')

/index.py

パッケージ内のモジュールは以下のようにして使用する。

from Hoge import dog

pochi = dog.Dog('pochi')
pochi.cry()

また、以下のようにして使用することもできる。

import Hoge.dog

pochi = Hoge.dog.Dog('pochi')
pochi.cry()