Help us understand the problem. What is going on with this article?

[AWS Lambda]S3にテキストファイルが置かれたらイベントドリブンに形態素解析(kuromoji)

More than 5 years have passed since last update.

はじめに

lambdaハンズオンを参考にlambdaを触ってみました。

プログラム書きたくて社会人になったのに気がついたらインフラエンジニアになってた人がアプリケーションもやれるのがAWSの良いところ!

Lambda in JAVA!

いろんなjavaアプリケーションをイベント駆動で実行できるようになりますね!(知ったかぶり

AWS Lambda Update – Run Java Code in Response to Events | AWS Official Blog

Amazon Web Services ブログ: 【AWS発表】AWS Lambdaのアップデート、イベントに応じてJavaコードを実行

なにを試そう…

パッと思いついた kuromoji.jar をLambda Functionの中で使ってみよう。

外部からS3 Bucketにテキストファイルが置かれたら自動で形態素解析して結果を別のbucketに出力する。
(それ何に使えるの?とか、cloudsearch使ったら?とか言わせない。)

やってみた

構成を考える

lambda-grant.png

※lambda:InvokeFunctionについては後述。特別設定は不要になったようです。

ここまで書いてみて必要そうな知識

  • Java SDKで、S3からObjectをgetする
  • Java SDKで、getしたObjectの文章を読込む
  • Kuromojiで形態素解析する
  • 結果をJava SDKでputする
  • Lambda Functionの書き方

本当にkuromoji.jar動かせんの?(Lambdaにアップロードできんの?)を調べる

容量制限の話。

http://docs.aws.amazon.com/lambda/latest/dg/limits.html

Input Limit
Compressed function .zip file 50 MB
Uncompressed function .zip file 250 MB
Invoke request body JSON 6 MB

圧縮して50MBまで。

kuromoji-0.7.7.jarが11M。いけそう。

どうやって作るのかを調べる(開発環境)

http://docs.aws.amazon.com/lambda/latest/dg/lambda-java-how-to-create-deployment-package.html

  • Creating a .jar Deployment Package Using Maven without any IDE (Java)
  • Creating a .jar Deployment Package Using Maven and Eclipse IDE (Java)
  • Creating a .zip Deployment Package (Java)
  • Authoring Lambda Functions Using Eclipse IDE and AWS SDK Plugin (Java)

2番目のMaven and Eclipse IDEでやってみようかと思います。

開発環境を準備する

作業PC

OS X Yosemite 10.10.3
homebrew, brew-cask インストール済み

Eclipseインストール

eclipse
brew cask install eclipse-platform

Mavenプロジェクトの作成

http://docs.aws.amazon.com/lambda/latest/dg/java-create-jar-pkg-maven-and-eclipse.html

を参考。

lambda002.png

Kuromojiを追加

http://www.atilika.org/
Maven artifact repository For ease of use with Maven or Ivy
を参照。

lambda003.png

lambda004.png

kuromoji-maven0001.png

aws toolkitインストール

インストール

以下を参考にインストール

http://docs.aws.amazon.com/ja_jp/AWSToolkitEclipse/latest/GettingStartedGuide/tke_setup_install.html

credential 設定

http://docs.aws.amazon.com/ja_jp/AWSToolkitEclipse/latest/GettingStartedGuide/tke_setup_creds.html

Toolkit for Eclipse に AWS アクセスキーを追加するにはを参照。

構成図に合わせてAWSの設定をする

S3 Bucket(ファイル受取用)

東京リージョンに作ります
~/.aws/configにregion設定済み。

makebucket
$ aws s3 mb s3://imura81gt-text-bucket

S3 Bucket(処理結果出力用)

形態素解析した結果はこのバケットに入れることにします。

makebucket
$ aws s3 mb s3://imura81gt-text-bucket-toknized

入力先bucketと出力先bucketを一緒にするとたぶん無限lambdaして大変なことになる。
-> EventSourceの設定でPrefix/Sufixの設定ができるようになったので、無限lambdaを防ぐ方法はある。

IAM role

exec Role

lambda-hanson資料

  • Lambda exec Roleの作成

を行う。 lambda_s3_exec_role とする。

Invoke Role

Event Sourceの設定の際にInvocation Roleの設定が必要でしたが、無くなってました。

coding

こんな感じでやりました。
(普段bashくらいしか書かないので、名前の付け方がおかしかったりしたら教えて下さい)

lambda1001.png

lambda1002.png

lambda1003.png

package net.outofbounds8.kuromoji.lambda;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.lambda.runtime.LambdaLogger;

import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;

import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.UUID;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;

import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;



// sample http://docs.aws.amazon.com/lambda/latest/dg/java-wt-s3-log-event-data.html

import com.amazonaws.services.s3.event.S3EventNotification.S3EventNotificationRecord;

public class LambdaFunctionHandler implements RequestHandler<S3Event, Object> {

    @Override
    public Object handleRequest(S3Event input, Context context) {
        context.getLogger().log("Input: " + input);
        context.getLogger().log("Input: " + input);

        LambdaLogger lambdaLogger = context.getLogger();

        S3EventNotificationRecord record = input.getRecords().get(0);
        lambdaLogger.log(record.getEventName()); //イベント名
        lambdaLogger.log(record.getS3().getBucket().getName()); //バケット名
        lambdaLogger.log(record.getS3().getObject().getKey()); //オブジェクトのキー(オブジェクト名)

        String bucketName = record.getS3().getBucket().getName();
        String key = record.getS3().getObject().getKey();
        S3Object object = null;

        String tld = "-toknized";

        StringBuilder buf = new StringBuilder();
        buf.append(bucketName);
        buf.append(tld);
        String outputBucketName = buf.toString();

        //AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
        AmazonS3 s3 = new AmazonS3Client();

        try {
            object = s3.getObject(new GetObjectRequest(bucketName, key));

            lambdaLogger.log("Content-Type: "  + object.getObjectMetadata().getContentType());
            lambdaLogger.log("object.getObjectContent:     " + object.getObjectContent());
            String text = getTextInputStream(object.getObjectContent());

            lambdaLogger.log(text); //イベント名

            File file = File.createTempFile("tokenize-", ".txt");
            file.deleteOnExit();

            Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");

            Tokenizer tokenizer = Tokenizer.builder().build();

            for (Token token : tokenizer.tokenize(text)) {
                System.out.println(token.getSurfaceForm() + "\t" + token.getAllFeatures());
                writer.write(token.getSurfaceForm() + "\t" + token.getAllFeatures() + "\n");
            }           

            writer.close();

            s3.putObject(new PutObjectRequest(outputBucketName, "toknized-" + key, file));


        } catch (AmazonServiceException ase) {
            System.out.println("Caught an AmazonServiceException, which means your request made it "
                    + "to Amazon S3, but was rejected with an error response for some reason.");
            System.out.println("Error Message:    " + ase.getMessage());
            System.out.println("HTTP Status Code: " + ase.getStatusCode());
            System.out.println("AWS Error Code:   " + ase.getErrorCode());
            System.out.println("Error Type:       " + ase.getErrorType());
            System.out.println("Request ID:       " + ase.getRequestId());
        } catch (AmazonClientException ace) {
            System.out.println("Caught an AmazonClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with S3, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message: " + ace.getMessage());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }



        return record.getS3().getObject().getKey();

        //http://dev.classmethod.jp/cloud/aws/aws-sdk-for-java-s3-create-download-application/

        // TODO: implement your handler
        //return null;
    }

    private static String getTextInputStream(InputStream input) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
        String text = "";
        String line = "";

        while (true) {
            line = reader.readLine();
            if (line == null) break;
            text = text + line;
        }
        return text;
    }

}

Eclipseからlambda functionをアップロード

upload

※東京リージョンが出る前に試した人はEclipseの
[help] - [check for update] からupdateを行うこと。
(デプロイする際に、古いものをしようしていると、東京リージョンが表示されないため)

kuromoji-upload0002.png

lambda005.png

lambda006.png

lambda007.png

lambda008-1.png

lambda008.png

lambda009.png

ManagementConsole 開く
lambda010.png
lambda011.png
lambda012.png

実行!

送るファイル

sumomo.txt
すもももももももものうち。
つまり、スモモは桃のうちであり
桃ももちろん桃のうちである、
という事である。
実際のところ、スモモはバラ科サクラ属であり、別名プラムという。
なので桃ではない。
このような文章のことを言葉遊びと言う。

s3になにもファイルがないのを確認

$ aws s3 ls s3://imura81gt-text-bucket/
$ aws s3 ls s3://imura81gt-text-bucket-toknized/

アップロード

$ aws s3 cp sumomo.txt s3://imura81gt-text-bucket/
upload: ./sumomo.txt to s3://imura81gt-text-bucket/sumomo.txt
$
$ aws s3 ls s3://imura81gt-text-bucket/
2015-08-01 00:58:21        334 sumomo.txt
$

形態素解析されたテキストが出力されていることを確認

$ aws s3 ls s3://imura81gt-text-bucket-toknized/
2015-08-01 00:58:33       3590 toknized-sumomo.txt
$

中身を表示してみる

$ aws s3 cp s3://imura81gt-text-bucket-toknized/toknized-sumomo.txt -
すもも   名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも  名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも  名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち  名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
。 記号,句点,*,*,*,*,。,。,。
つまり   接続詞,*,*,*,*,*,つまり,ツマリ,ツマリ
、 記号,読点,*,*,*,*,、,、,、
スモモ   名詞,一般,*,*,*,*,スモモ,スモモ,スモモ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち  名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
あり  助動詞,*,*,*,五段・ラ行アル,連用形,ある,アリ,アリ
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もちろん    副詞,一般,*,*,*,*,もちろん,モチロン,モチロン
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち  名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある  助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
、 記号,読点,*,*,*,*,、,、,、
という   助詞,格助詞,連語,*,*,*,という,トイウ,トユウ
事 名詞,非自立,一般,*,*,*,事,コト,コト
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある  助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
。 記号,句点,*,*,*,*,。,。,。
実際  副詞,助詞類接続,*,*,*,*,実際,ジッサイ,ジッサイ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
ところ   名詞,非自立,副詞可能,*,*,*,ところ,トコロ,トコロ
、 記号,読点,*,*,*,*,、,、,、
スモモ   名詞,一般,*,*,*,*,スモモ,スモモ,スモモ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
バラ  名詞,一般,*,*,*,*,バラ,バラ,バラ
科 名詞,接尾,一般,*,*,*,科,カ,カ
サクラ   名詞,一般,*,*,*,*,サクラ,サクラ,サクラ
属 名詞,サ変接続,*,*,*,*,属,ゾク,ゾク
で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
あり  助動詞,*,*,*,五段・ラ行アル,連用形,ある,アリ,アリ
、 記号,読点,*,*,*,*,、,、,、
別名  名詞,一般,*,*,*,*,別名,ベツメイ,ベツメイ
プラム   名詞,一般,*,*,*,*,プラム,プラム,プラム
と 助詞,格助詞,引用,*,*,*,と,ト,ト
いう  動詞,自立,*,*,五段・ワ行促音便,基本形,いう,イウ,イウ
。 記号,句点,*,*,*,*,。,。,。
なので   接続詞,*,*,*,*,*,なので,ナノデ,ナノデ
桃 名詞,一般,*,*,*,*,桃,モモ,モモ
で 助詞,格助詞,一般,*,*,*,で,デ,デ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
ない  助動詞,*,*,*,特殊・ナイ,基本形,ない,ナイ,ナイ
。 記号,句点,*,*,*,*,。,。,。
この  連体詞,*,*,*,*,*,この,コノ,コノ
よう  名詞,非自立,助動詞語幹,*,*,*,よう,ヨウ,ヨー
な 助動詞,*,*,*,特殊・ダ,体言接続,だ,ナ,ナ
文章  名詞,一般,*,*,*,*,文章,ブンショウ,ブンショー
の 助詞,連体化,*,*,*,*,の,ノ,ノ
こと  名詞,非自立,一般,*,*,*,こと,コト,コト
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
言葉  名詞,一般,*,*,*,*,言葉,コトバ,コトバ
遊び  名詞,一般,*,*,*,*,遊び,アソビ,アソビ
と 助詞,格助詞,引用,*,*,*,と,ト,ト
言う  動詞,自立,*,*,五段・ワ行促音便,基本形,言う,イウ,イウ
。 記号,句点,*,*,*,*,。,。,。
$

まとめ

Amazon API Gateway と組み合わせれば
形態素解析APIが作れそう!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした