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

## 本記事で目指す構成

![lamda-csv-to-json.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/cdf24a71-9d1c-b669-9392-d6434acf726a.png)

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

## 使用技術

言語: Python 3.8
AWS: S3、Lambda

## 下準備

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

### IAMユーザーを作成

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

![スクリーンショット 2021-01-19 19.22.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/58a395ce-6ff7-dc18-e6f1-c322dae62146.png)

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

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

![スクリーンショット 2021-01-19 19.28.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/3cece582-727a-b2ae-489e-53acfa5cacb7.png)

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

![スクリーンショット 2021-01-19 19.30.55_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/ad00e029-444f-77c2-5222-733b6208f10e.jpeg)

作成完了すると

- アクセスキー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
```

![スクリーンショット 2021-01-19 20.04.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/61865ae7-b848-89cc-963a-1833cdd8e6d3.png)

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

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

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


### IAMロールを作成

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

![スクリーンショット 2021-01-19 19.05.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/9430bce2-f8de-e036-25fa-02577d21280f.png)

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

- AmazonS3FullAccess
- AWSLambdaBasicExecutionRole

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

![スクリーンショット 2021-01-19 19.10.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/49b421dc-d464-fc8c-9852-fe852cdcd7e9.png)

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

## 実装

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

### Lambda関数を作成

![スクリーンショット 2021-01-19 20.12.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a47933f8-6e55-0e39-1fc2-68a57031c77d.png)

「Lambda」→「関数の作成」

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

### トリガーを作成

![スクリーンショット 2021-01-19 20.19.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/9364b89a-08a5-0048-a5ac-ab03fd168d7f.png)

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

![スクリーンショット 2021-01-19 20.22.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/4feedc10-0125-373b-9683-f1fdb5c80855.png)

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

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

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

### コード

```python
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でファイルをアップロードしてみましょう。

![スクリーンショット 2021-01-19 20.39.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a519a55f-fa6e-017f-6fc6-deb309aae24d.png)
![スクリーンショット 2021-01-19 20.39.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/995c5261-1bf2-2ced-0ba1-e6f79f10ee29.png)

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

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

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

## あとがき

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

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

## 参照記事

[Convert CSV to JSON files with AWS Lambda and S3 Events](https://sysadmins.co.za/convert-csv-to-json-files-with-aws-lambda-and-s3-events/)