AWS LambdaにMecabを乗せてPythonで動かす


実装するにあたり[こちらのサイト](http://dev.classmethod.jp/cloud/aws-lambda-with-mecab/)を参考にさせて頂きました！
ありがとうございます！

pythonのバージョンは2.7
localの環境はWindows bashです。

## はじめに
Mecabはコードにネイティブバイナリを使用しているため、Lambdaの実行環境と同じ環境でデプロイパッケージを作成する必要があります。
（ソースはここです。[Lambdaの実行環境ドキュメント](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/current-supported-versions.html)）
なので、EC2でAmazon Linuxインスタンスを立て、その中でデプロイパッケージを作成していきます。

## 必要なファイル及び作成環境の準備
まず必要なファイルをローカル上にDLします。
① mecab-0.996.tar.gz
② mecab-ipadic-2.7.0-20070801.tar.gz
上記2つは[ここ](http://taku910.github.io/mecab/#download)からDLできます
③ mecab-python-0.996.tar.gz
[ここ](https://pypi.python.org/pypi/mecab-python/0.996)からDLできます。

次にlinuxインスタンスにsshでログインします。
やり方分からないよって方は、[AWSのドキュメント](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html)の「Linuxインスタンスへの接続」の項を参照していただけると分かると思います！

Linuxインスタンスは、立てたばかりの状態ではコンパイラなどが入っていないのでインストールします。

```bash:Linuxインスタンス上
[ec2-user ~]$ sudo yum groupinstall "Development Tools"
```
そしてLinuxインスタンスのホームディレクトリにmecab-functionディレクトリを作成します。

```bash:Linuxインスタンス上
$mkdir mecab-function
```


先ほどDLした①〜③のファイルを全てLinuxインスタンスのホームディレクトリに送信します。
（こちらも詳細は[ドキュメント](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html)の「scpを利用してファイルを転送するには」の項を参照していただけると分かると思います。）

```bash:ローカル上
scp -i /path/秘密キーファイルの名前 /path/mecab-0.996.tar.gz ec2-user@インスタンスのパブリックDNS名:~

scp -i /path/秘密キーファイルの名前 /path/mecab-ipadic-2.7.0-20070801.tar.gz ec2-user@インスタンスのパブリックDNS名:~

scp -i /path/秘密キーファイルの名前 /path/mecab-python-0.996.tar.gz ec2-user@インスタンスのパブリックDNS名:~
```


ここまで出来たら、Linuxインスタンスのホームディレクトリには以下のファイルやディレクトリが存在してると思います。
>mecab-function/
 mecab-0.996.tar.gz
 mecab-ipadic-2.7.0-20070801.tar.gz
 mecab-python-0.996.tar.gz
	

次は、今落としたmecabなどをディレクトリにインストールしていきます。

## ディレクトリへのインストール
ここからは、Linuxインスタンス上での操作となります。
まず、mecabとmecab-ipadicを解凍してインストールしていきます。

① mecabのインストール

```bash:
$tar zvxf mecab-0.996.tar.gz
$cd mecab-0.996
$./configure --prefix=$DIR_HOME/local --with-charset=utf8
$make
$make install
```

"$DIR_HOME"は、mecab-functionディレクトリのパスです。
mecab-functionディレクトリ内で以下のコマンドを実行すると、パスを取得できます。

```bash
$pwd
```

"--prefix=ディレクトリのパス"で、mecabをインストールするディレクトリを指定しています。

"--with-charset=utf8"は、mecabをutf8で使用するという宣言をしています。
この宣言をしないと、mecabが上手くparseできなかったり、エラーが出たりするので注意してください。
ちなみに、"--enable-utf8-only"とは違うのでこちらも注意してください。

"make install"が完了したら、mecab-0.996ディレクトリから、ホームディレクトリに戻ります。

② mecab-ipadicのインストール

```bash:
$tar zvxf mecab-ipadic-2.7.0-20070801.tar.gz
$cd mecab-ipadic-2.7.0-20070801

# mecabの場所をPATHに追加
$export PATH=$DIR_HOME/local/bin:$PATH

$./configure --prefix=$DIR_HOME/local --with-charset=utf8
$make
$make install
```

"$DIR_HOME"など、①と同じです。
"make install"が終了したら、ホームディレクトリに戻ります。
ここで、"mecab"コマンドを実行した時に動作すればmecabとmecab-ipadicのインストールは正しくできています。

③mecab-pythonのインストール

```bash:
$pip install mecab-python-0.996.tar.gz -t $DIR_HOME/lib
```

pipは、-tでライブラリのインストール先を指定できます。

ここまで出来ると、mecab-function内は以下の構造になっていると思います。

>mecab-function
	|- lib/
	|- local/
	|- exclude.lst
	|- function.py

function.pyとexclude.lstって何？ってなりますよね？
次でその2つのファイルについて説明します。

## function.pyの説明

Lambdaのハンドラー関数を含むpythonのコードを説明します。
今回は、入力された日本語を単に分かち書きして出力するハンドラー関数を作成しました。

```python:function.py
#coding: utf-8

import json
import os
import ctypes

#ライブライのパスを取得
libdir = os.path.join(os.getcwd(), "local", "lib")
libmecab = ctypes.cdll.LoadLibrary(os.path.join(libdir, "libmecab.so.2"))

import MeCab

#mecabの辞書(ipadic)へのパスを取得
dicdir = os.path.join(os.getcwd(), "local", "lib", "mecab", "dic", "ipadic")
#mecabのrcファイルへのパスを取得
rcfile = os.path.join(os.getcwd(), "local", "etc", "mecabrc")
tagger = MeCab.Tagger("-d{} -r{}".format(dicdir, rcfile))


def handler(event, context):
	"""
	event = {
		"sentence": 分かち書きしたい文章
	}
	"""
    sentence = event.get("sentence")
    encode_sentence = sentence.encode("utf_8")
    node = tagger.parseToNode(encode_sentence)
    result = []
    while node:
        surface = node.surface
        if surface != "":
            test_list = [surface]
            print surface
            print test_list
            decode_surface = surface.decode("utf_8")
            result.append(decode_surface)
        node = node.next
    return result

```

"ctypes.cdll.LoadLibrary()"で、動的リンクライブラリをロードしています。
また、MeCab.Taggerクラスのオブジェクトを作成する際に、辞書とmecabrcファイルへのパスを指定してあげる必要があります。

## exclude.lstの説明

zipファイル作成時に除外するファイルの一覧です。

```python:exclude.txt
*.dist-info/*
*.egg-info
*.pyc
exclude.lst
local/bin/*
local/include/*
local/libexec/*
local/share/*
```

## デプロイパッケージの作成

いよいよデプロイパッケージの作成です。
mecab-function/libにある
>_MeCab.so
 MeCab.py
 MeCab.pyc
 
の3つのファイルをmecab-fucntionに移します。
ここまで行えば、以下のような感じになってると思います。

>mecab-function
	|- lib/
	|- local/
	|- _MeCab.so
	|- exclude.lst
	|- function.py
	|- MeCab.py
	|- MeCab.pyc
	
そして、mecab-functionディレクトリで以下のコマンドを実行すると、mecab-function.zipが作成されます。

```bash:
$zip -r9 mecab-function.zip * -x@exclude.lst
```

mecab-function.zipが以下のような構造になっていたら完成です。

>mecab-function
	|- lib/
	|- local/
	|- _MeCab.so
	|- function.py
	|- MeCab.py
	|- MeCab.pyc

あとは、mecab-function.zipをLambdaにあげれば完了です！
Linuxインスタンス上にあるmecab-function.zipをscpコマンドを使ってローカルに転送してAWS Lambdaのコンソール画面から直接あげてもいいですし、S3に飛ばしてからあげてもいいと思います。
自分は、ローカルに一旦転送しました。

ローカルに転送するには、ローカルで以下のコマンドを実行します。

```bash:ローカル上
scp -i /path/秘密キーファイルの名前 ec2-user@インスタンスのパブリックDNS名:~/mecab-function/mecab-function.zip /転送したい場所のpath(ローカル上)/mecab-function.zip
```

このコマンドの詳しい内容は、おなじみ[AWSのドキュメント](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html)の「scpを利用してファイルを転送するには」を参照していただければと思います。

## まとめ

最後に重要なポイントをまとめたいと思います。

① デプロイパッケージは、Linuxインスタンス上で作成する！
② mecabをimportする時に、動的リンクライブラリをロードする。
③ MeCab.Taggerでオブジェクト作成時に、辞書とmecabrcファイルへのパスを渡してあげる。
④デプロイパッケージの構造は、この記事で示したようにする。

ここまで読んで頂きありがとうございました。