【Python】S3にアップロードされたCSVファイルをAWS LambdaでJSONファイルに変換する


本記事で目指す構成

S3にCSVファイルをアップロード → Lambda起動 → JSONファイルに変換

使用技術

言語: Python 3.8
AWS: S3、Lambda

下準備

まず最初にIAMユーザーやIAMロール、S3バケットなどの準備を行います。

IAMユーザーを作成

今回はAWS CLIを使って作業していくので、専用のIAMユーザーを作成します。

「IAM」→「ユーザー」→「ユーザーを追加」

ユーザー名: 任意
アクセスの種類: 「プログラムによるアクセス」にチェック

今回はS3バケットの作成、ファイルのアップロードや削除などS3に関する基本的な動作を行いたいので「AmazonS3FullAccess」ポリシーをアタッチしておきます。

作成完了すると

  • アクセスキーID
  • シークレットアクセスキー

の2つが発行されるのでメモに控えておきましょう。

$ aws configure --profile s3-lambda

AWS Access Key ID [None]: ***************** # 自分のアクセスキーIDを入力
AWS Secret Access Key [None]: ************************** # 自分のシークレットアクセスキーを入力
Default region name [None]: ap-northeast-1
Default output format [None]: json

ターミナルで上記コマンドを打つと対話形式で情報を聞かれるので、指示に従いながら入力していきます。

S3バケットを作成

先ほど設定したAWS CLIを使ってちゃちゃっと作成してしまいます。

$ aws --profile s3-lambda s3 mb s3://test-bucket-for-converting-csv-to-json-with-lambda

make_bucket: test-bucket-for-converting-csv-to-json-with-lambda

バケット名は全世界においてユニークでないといけないので、各自オリジナルのものを考えてください。

テスト用CSVファイルを作成して試しにアップロードしてみる

$ mkdir ./workspace/
$ cat > ./workspace/test.csv << EOF
heredoc> Name,Age,Country
heredoc> Taro,20,Japan
heredoc> EOF
$ aws --profile s3-lambda s3 sync ./workspace s3://test-bucket-for-converting-csv-to-json-with-lambda

upload: ./test.csv to s3://test-bucket-for-converting-csv-to-json-with-lambda/test.csv

ちゃんとバケット内に入っていれば成功。

$ aws --profile s3-lambda s3 rm s3://test-bucket-for-converting-csv-to-json-with-lambda/test.csv

動作確認が済んだので消しておきます。

IAMロールを作成

Lambdaに割り当てる用のIAMロールを作成します。

「IAM」→「ロール」→「ロールの作成」

  • AmazonS3FullAccess
  • AWSLambdaBasicExecutionRole

今回は上記2つのポリシーがあればOK。

適当に名前や説明文を入力して作成してください。

実装

下準備が終わったのでいよいよここから実装を行っていきます。

Lambda関数を作成

「Lambda」→「関数の作成」

  • オプション: 一から作成
  • 関数名: 任意
  • ランタイム: Python 3.8
  • 実行ロール: 既存のロール(先ほど作した「s3-lambda」)
  • その他: デフォルトでOK

トリガーを作成

どんなイベントが発生した際にLambdaを起動させるのかを決めるために、「Configuration」→「トリガーを追加」へ進んでください。

必要事項を記入していきます。

  • トリガー: S3
  • バケット: 先ほど作成したバケット名
  • イベントタイプ: すべてのオブジェクト作成イベント
  • プレフィックス: input/
  • サフィックス: .csv

今回は「input」というフォルダ配下に「.csv」ファイルがアップロードされた事を検知した後、Lambdaを起動させる事を想定しています。

コード

import json
import csv
import boto3
import os
from datetime import datetime, timezone, timedelta

s3 = boto3.client('s3')

def lambda_handler(event, context):

    json_data = []

    # TZを日本に変更
    JST = timezone(timedelta(hours=+9), 'JST')
    timestamp = datetime.now(JST).strftime('%Y%m%d%H%M%S')

    # 一時的な読み書き用ファイル(後で消す)
    tmp_csv = '/tmp/test_{ts}.csv'.format(ts=timestamp)
    tmp_json = '/tmp/test_{ts}.json'.format(ts=timestamp)

    # 最終的な出力ファイル
    outputted_json = 'output/test_{ts}.json'.format(ts=timestamp)

    for record in event['Records']:
        bucket_name = record['s3']['bucket']['name']
        key_name = record['s3']['object']['key']

    s3_object = s3.get_object(Bucket=bucket_name, Key=key_name)
    data = s3_object['Body'].read()
    contents = data.decode('utf-8')

    try:
        with open(tmp_csv, 'a') as csv_data:
            csv_data.write(contents)

        with open(tmp_csv) as csv_data:
            csv_reader = csv.DictReader(csv_data)
            for csv_row in csv_reader:
                json_data.append(csv_row)

        with open(tmp_json, 'w') as json_file:
            json_file.write(json.dumps(json_data))

        with open(tmp_json, 'r') as json_file_contents:
            response = s3.put_object(Bucket=bucket_name, Key=outputted_json, Body=json_file_contents.read())

        os.remove(tmp_csv)
        os.remove(tmp_json)

    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e

これでS3バケット名「test-bucket-for-converting-csv-to-json-with-lambda/input/」にCSVファイルがアップロードされると「test-bucket-for-converting-csv-to-json-with-lambda/output/」にJSON形式に変換されたファイルが吐き出されるようになります。

$ aws --profile s3-lambda s3 sync ./workspace s3://test-bucket-for-converting-csv-to-json-with-lambda/input

upload: ./test.csv to s3://test-bucket-for-converting-csv-to-json-with-lambda/input/test.csv

再度、AWS CLIでファイルをアップロードしてみましょう。


バケットを確認すると「output」というフォルダが新たに作成され、中にJSONファイルが入っているはず。

[
    {
        "Name": "Taro",
        "Age": "20",
        "Country": "Japan"
    }
]

中身を確認し、しっかりとJSON形式に変換されていれば完了です。

あとがき

お疲れ様でした。今回はCSV→JSONへの変換でしたが、同じ要領で他のパターンも色々実現できると思います。

少しでも参考になれば幸いです。

参照記事

Convert CSV to JSON files with AWS Lambda and S3 Events