この記事では、AWS SAMを使用してS3バケットにファイルがアップロードされた際に自動的にAWS Textractを利用してテキストを抽出するLambda関数を作成する方法を説明します。
このテンプレートは以下のリソースを作成します。
S3バケット: ファイルのアップロードをトリガーするための入力バケットです。
バケット名は{AccountId}-{Region}-input-bucketという形式で生成されます。
Lambda関数: S3バケットにファイルがアップロードされるとトリガーされる関数です。
この関数はPython 3.11で動作し、Textractを使用してアップロードされたファイルからテキストを抽出します。
IAMロール: Lambda関数が適切な権限を持つためのロールです。
このロールには、Lambdaの基本実行権限、S3へのフルアクセス権限、Textractへのフルアクセス権限が含まれます。
このテンプレートの構成と実装方法を解説し、最後に、実際に動作確認を行い、S3バケットにファイルをアップロードしてTextractによるテキスト抽出が成功することを確認します。
AWS SAM をインストール
> pip install aws-sam-cli
SAM プロジェクトの作成
> sam init
Which template source would you like to use?
1 - AWS Quick Start Templates
Choose an AWS Quick Start application template
1 - Hello World Example
Use the most popular runtime and package type? (Python and zip) [y/N]: y
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: N
Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]: N
Would you like to set Structured Logging in JSON format on your Lambda functions? [y/N]: N
Project name [sam-app]: 好きなプロジェクト名
Lambda関数のコードを修正
S3バケットにアップロードしたファイルを textract.detect_document_text に渡して結果を extracted フォルダ内に出力します。
# S3にファイルがアップロードされたのを検知してTextractを実行する
import boto3
import json
def lambda_handler(event, context):
s3 = boto3.client("s3")
textract = boto3.client("textract")
for record in event["Records"]:
bucket = record["s3"]["bucket"]["name"]
key = record["s3"]["object"]["key"]
print(f"record: {record}")
print(f"bucket: {bucket}")
print(f"key: {key}")
if "/" not in key:
response = textract.detect_document_text(
Document={"S3Object": {"Bucket": bucket, "Name": key}}
)
textracted_text = ""
for item in response["Blocks"]:
if item["BlockType"] == "LINE":
textracted_text += item["Text"] + "\n"
output_key = f"extracted/{key.split('/')[-1]}.txt"
print(f"output_key: {output_key}")
s3.put_object(Bucket=bucket, Key=output_key, Body=textracted_text)
template.yamlファイル修正
Role: !GetAtt LambdaExecutionRole.Arn で S3 と Textract の権限を Lambdaに設定します。
# S3にファイルがアップロードされたのを検知してLambdaからTextractを実行する
AWSTemplateFormatVersion: '2010-09-09' # テンプレートのバージョンを指定
Transform: 'AWS::Serverless-2016-10-31' # AWS SAM を使用するための変換ルール
Description: 'S3 upload trigger for AWS Textract text extraction' # テンプレートの説明文を指定
Resources:
MyBucket:
Type: 'AWS::S3::Bucket' # S3 バケットのリソースタイプを指定
Properties:
BucketName: !Sub '${AWS::AccountId}-${AWS::Region}-input-bucket' # バケット名を指定
S3EventProcessorFunction:
Type: 'AWS::Serverless::Function' # サーバーレスの Lambda 関数を指定
Properties:
Handler: app.lambda_handler # Lambda 関数のハンドラ名を指定
Runtime: python3.11 # Lambda 関数の実行ランタイムを指定
Role: !GetAtt LambdaExecutionRole.Arn # Lambda 関数の実行ロールを参照
CodeUri: hello_world/ # Lambda 関数のコードが配置されているディレクトリを指定
Timeout: 30 # Lambda 関数のタイムアウトを指定
Events:
S3Event:
Type: S3 # S3 イベントトリガーを指定
Properties:
Bucket: !Ref MyBucket # トリガーする S3 バケットを参照
Events: s3:ObjectCreated:* # オブジェクトが作成されたときにトリガーするイベントを指定
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AmazonTextractFullAccess
ビルド実行
> sam build
デプロイ実行
デプロイの注意点として、Textract は日本のリージョンには対応していない為、今回はバージニア北部リージョンを選択して実行しています。
> sam deploy --guided
...
AWS Region [us-east-1]: us-east-1
...
S3へファイルのアップロード
問題なくデプロイが完了したらS3へファイルをアップロードして動作確認を行います。
日本語はうまく変換できない為、今回はコードのスクリーンショットを使います。
バケットが2つできていると思いますが、[us-east-1-input-bucket] とついている方にアップロードしてください。
少し待ってからオブジェクトの更新を行ってください。
extracted/ フォルダが自動で作成されます。
中にテキストファイルが作成されているので、ダウンロードして開いてみます。
import boto3
import json
Codeium: Refactor | Explain I Generate Docstring IX
def lambda_handler (event, context):
s3 = boto3.client("s3")
textract = boto3.client("textract")
-
for record in event["Records"]:
bucket = record["s3"]["bucket"]["name"]
key = record["s3"]["object"]["key"]
'
response = textract.detect_document_text(
I
Document={"S30bject": {"Bucket": bucket, "Name": key}}
textracted_text="
for item in response["Blocks"]
ifitem["BlockType"] == "LINE":
|
textracted_text +=item["Text"] + "\n"
output_key = f"extracted/{key.split('/")[-1]}.txt"
s3.put_object(Bucket=bucket,Key=output_key,Body=textracted_text)
一部縦線等が変換されてしまいましたが、ほぼ同じ内容が出力されました。
ログの確認
最後にCloudWatchでログの確認を行います。
ロググループの中に/aws/lambda/~が作成されているので中を確認すると、先ほどダウンロードしたファイル名が確認できました。
エラーが出た場合も同様にログを確認してください。
※ログが出力されていない場合は、S3のputイベントに対しLambdaが動作していないので、template.yaml の設定を確認してください。
まとめ
以前の記事で紹介したS3へのアップロードイベントに対してLambda関数を実行させる方法は、非常に便利であると改めて感じました。
Lambda関数やtemplate.yamlの一部を変更するだけで、さまざまな処理を柔軟に実行できることは大きな利点で、多くのユースケースに簡単に対応できると思います。