LambdaでExcelを操作してみた(openpyxl)

LambdaでExcelを操作してみた(openpyxl)

LambdaでExcelファイルを操作してみました。 S3バケットのエクセルを読み込み、編集して、新規ファイル保存し、S3バケットに格納します。
Clock Icon2024.07.24

LambdaでExcelを操作したいことってありますよね。というわけで、試してみました。

  1. S3バケットのExcelをgetする
  2. LambdaでExcelを更新する
  3. S3バケットにExcelをputする

おすすめの方

  • PythonでExcelを操作したい方
  • LambdaでExcelを操作したい方

S3バケットやLambdaを作成する

sam init

sam init \
    --runtime python3.11 \
    --name lambda-excel-sample \
    --app-template hello-world \
    --no-tracing \
    --no-application-insights \
    --structured-logging \
    --package-type Zip

requirements.txtにopenpyxlを記載する

requirements.txt
openpyxl

Lambdaコード

S3バケットからエクセルを取得し、更新し、別ファイル保存し、S3に格納しています。

今回は簡略化していますが、実際には次の対応をすると良いと思います。

  • 別ファイル保存時のファイル名を重複しないようにする(UUIDを使うなど)
  • Lambdaの/tmpに保存したファイル(別ファイル保存)は、最後に削除する
    • 同じLambdaがたくさん実行された場合、容量が不足する可能性があるため
app.py
import os
import boto3

from datetime import datetime, timedelta, timezone

from openpyxl import load_workbook

# DynamoDBなどから取得したデータを想定しています
DATA = [
    {"timestamp": 1690188000000, "temperature": 32.9, "humidity": 64},
    {"timestamp": 1690191000000, "temperature": 30.7, "humidity": 58},
    {"timestamp": 1690194000000, "temperature": 34.1, "humidity": 54},
    {"timestamp": 1690197000000, "temperature": 36.5, "humidity": 59},
    {"timestamp": 1690200000000, "temperature": 33.3, "humidity": 66},
    {"timestamp": 1690203000000, "temperature": 29.8, "humidity": 49},
]

JST = timezone(timedelta(hours=+9), "JST")

s3 = boto3.client("s3")

BUCKET_NAME = os.getenv("BUCKET_NAME")
KEY_NAME = "sample.xlsx"

OUTPUT_FILE_NAME = "output.xlsx"

def lambda_handler(event, context):
    local_file_path = download_file(BUCKET_NAME, KEY_NAME)
    if local_file_path is None:
        return {
            "statusCode": 500,
        }

    output_file_path = os.path.join("/tmp", OUTPUT_FILE_NAME)

    res = make_excel(local_file_path, output_file_path)
    if res is None:
        return {
            "statusCode": 500,
        }

    upload_file(BUCKET_NAME, OUTPUT_FILE_NAME, output_file_path)

    return {
        "statusCode": 200,
    }

def download_file(bucket_name: str, file_name: str) -> str | None:
    local_file_path = os.path.join("/tmp", file_name)

    s3.download_file(bucket_name, file_name, local_file_path)

    if os.path.exists(local_file_path):
        return local_file_path

    return None

def make_excel(local_file_path: str, output_file_path: str) -> str | None:
    wb = load_workbook(filename=local_file_path)
    ws_test1 = wb["テスト1"]

    for i, d in enumerate(DATA, start=2):
        ws_test1[f"A{i}"] = datetime.fromtimestamp(
            d["timestamp"] / 1000, tz=JST
        ).strftime("%Y-%m-%d %H:%M:%S")
        ws_test1[f"B{i}"] = d["temperature"]
        ws_test1[f"C{i}"] = d["humidity"]

    wb.save(output_file_path)

    if os.path.exists(output_file_path):
        return output_file_path

    return None

def upload_file(bucket_name: str, file_name: str, file_path: str) -> None:
    s3.upload_file(file_path, bucket_name, file_name)

デプロイ

sam build --use-container
sam deploy \
    --guided \
    --region ap-northeast-1 \
    --stack-name lambda-excel-sample-stack

適当なExcelを用意する

2つのシートを持つExcelを用意しました。

  • テスト1
    • 時系列にデータを入れる
  • テスト2
    • 最大値などを表示する
    • MAX()AVERAGE()MIN()を使う

_01_excel

_02_excel

ExcelをS3バケットに格納する

aws s3 cp sample.xlsx s3://excel-xxx-ap-northeast-1-bucket

_11_s3

動作を確認する

Lambdaを実行する

今回はAPI Gateway経由で動くようにしているので、curlでgetアクセスします。

curl https://xxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/

マネジメントコンソールで直接実行してもOKです。

S3バケットを確認する

Excelファイルが増えていました。

_21_s3

Excelファイルを確認する

期待通りに更新されていました。

_31_excel

_32_excel

さいごに

LambdaでExcelを操作してみました。
テンプレートとなるExcelファイルをS3バケットに格納しておき、任意の場所にデータを追加して別ファイル化して保存する場合などで使えると思います。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.