0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SAM/lambda/Pythonを使ってエクセル操作してみたいな〜というお話

Last updated at Posted at 2025-01-20

最初に

pythonでエクセルやらスクレーピングやらを簡単にやってみたいなと思い、
まずはエクセルで簡単なファイル操作をしてみました。

今回、lambda以外は全て触るのが初めてなので微妙な記述等あるかと思いますが、
ご容赦いただければ幸いです。またSAMやAWS CLIなどのインストール等については割愛しておりますので、同じように試される方は適宜検索してみてください。

今回実現したいのは、
以下のようにcurlでエクセルファイルを添付して、APIGatewayを叩いて、lambdaで処理して、そのエクセルファイルから値を抽出して返却する、というシンプルなものです。

が、思った以上に大変でした。。。

スクリーンショット 2025-01-20 11.34.07.png

SAM

では早速SAMから定義していきます。

sam initを実行し、セットアップのために色々と聞かれるので、答えていく。

.terminal
$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
	1 - Hello World Example
	2 - Data processing
	3 - Hello World Example with Powertools for AWS Lambda
	4 - Multi-step workflow
	5 - Scheduled task
	6 - Standalone function
	7 - Serverless API
	8 - Infrastructure event management
	9 - Lambda Response Streaming
	10 - GraphQLApi Hello World Example
	11 - Full Stack
	12 - Lambda EFS example
	13 - Serverless Connector Hello World Example
	14 - Multi-step workflow with Connectors
	15 - DynamoDB Example
	16 - Machine Learning
Template: 1

Use the most popular runtime and package type? (python3.13 and zip) [y/N]: N

Which runtime would you like to use?
	1 - dotnet8
	2 - dotnet6
	3 - go (provided.al2)
	4 - go (provided.al2023)
	5 - graalvm.java11 (provided.al2)
	6 - graalvm.java17 (provided.al2)
	7 - java21
	8 - java17
	9 - java11
	10 - java8.al2
	11 - nodejs22.x
	12 - nodejs20.x
	13 - nodejs18.x
	14 - python3.9
	15 - python3.8
	16 - python3.13
	17 - python3.12
	18 - python3.11
	19 - python3.10
	20 - ruby3.3
	21 - ruby3.2
	22 - rust (provided.al2)
	23 - rust (provided.al2023)
Runtime: 15

# ここで今回はイメージを選択しました。
What package type would you like to use?
	1 - Zip
	2 - Image
Package type: 2

Based on your selections, the only dependency manager available is pip.
We will proceed copying the template using pip.

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]: y
Structured Logging in JSON format might incur an additional cost. View https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-pricing for more details

Project name [sam-app]: excel-app

    -----------------------
    Generating application:
    -----------------------
    Name: excel-app
    Base Image: amazon/python3.8-base
    Architectures: x86_64
    Dependency Manager: pip
    Output Directory: .
    Configuration file: excel-app/samconfig.toml

    Next steps can be found in the README file at excel-app/README.md
    

Commands you can use next
=========================
[*] Create pipeline: cd excel-app && sam pipeline init --bootstrap
[*] Validate SAM template: cd excel-app && sam validate
[*] Test Function in the Cloud: cd excel-app && sam sync --stack-name {stack-name} --watch

すると以下のようなディレクトリとファイルが生成されます。
ここで2点追加操作します。

  • 送信用のエクセルファイルを追加(何でもOK)
  • hello_wordからexcel_manipulatorにディレクトリ名を変更

ここのポイントとしてはWhat package type would you like to use?の質問でImageを使用することです。

cd excel-app
tree

.
├── Book1.xlsx # 検証用に手動で後から追加しました。
├── README.md
├── __init__.py
├── events
│   └── event.json
├── excel_manipulator # hello_worldから変更
│   ├── Dockerfile
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── samconfig.toml
├── template.yaml
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_handler.py

template.yaml

次にtemplate.yamlを変更します。

.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  python3.8

  Sample SAM Template for excel-app

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    # 時間がかかる処理をしたい場合は、ここを伸ばす必要があります。
    Timeout: 3
    MemorySize: 128

    # You can add LoggingConfig parameters such as the Logformat, Log Group, and SystemLogLevel or ApplicationLogLevel. Learn more here https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-loggingconfig.
    LoggingConfig:
      LogFormat: JSON
Resources:
  # RestAPIの定義
  ExcelManipulatorApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: default
      BinaryMediaTypes:
        - '*~1*'

  ExcelManipulatorFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: 
    Properties:
      PackageType: Image
      Architectures:
      - x86_64
      Events:
        ExcelManipulatorApi:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /excel_manipulator
            Method: post
            RestApiId: !Ref ExcelManipulatorApi

    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./excel_manipulator # ここをhello_worldから変更する
      DockerTag: python3.8-v1

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  ExcelManipulatorApi:
    Description: API Gateway endpoint URL for Prod stage for ExcelManipulator Function
    Value: !Sub "https://${ExcelManipulatorApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/excel_manipulator/"
  ExcelManipulatorFunction:
    Description: ExcelManipulator Lambda Function ARN
    Value: !GetAtt ExcelManipulatorFunction.Arn
  ExcelManipulatorFunctionIamRole:
    Description: Implicit IAM Role created for ExcelManipulator Function
    Value: !GetAtt ExcelManipulatorFunctionRole.Arn

細かくは解説しませんが、ポイントが数点あります(めちゃくちゃ沼った。。。)

BinaryMediaTypesを指定する

これをAPIに指定しないといけません。
なぜなら、RestAPIでは、デフォルトではUTF-8のテキストのペイロードしかサポートしておらず、これを設定しないとめちゃくちゃ文字化けして500エラーが返却されます。

こんな感じで文字化けします。
スクリーンショット 2025-01-20 18.21.34.png

HTTPAPIにすれば問題ないのですが、今回はRESTAPIで進めるためにこの設定を付与しました。

また以下のドキュメントに書かれている通り/~1と書く必要があるので注意が必要です。
https://docs.aws.amazon.com/apigateway/latest/api/API_RestApi.html#apigw-Type-RestApi-binaryMediaTypes:~:text=Required%3A%20No-,binaryMediaTypes,-The%20list%20of

app.py

次にコードを変更します。特に注意点はありません。
Base64でエンコードされているので、それをデコードする、くらいでしょうか。

.py
import json
import pandas as pd
import base64
from io import BytesIO

def lambda_handler(event, context):
    print("event", event)
    # Base64エンコードされたエクセルデータを取得
    body = event['body']
    print("body_data", body)
    
    # Base64デコードしてバイナリデータに変換
    excel_data = base64.b64decode(body)

    # BytesIOを使ってメモリ上でエクセルファイルを開く
    excel_file = BytesIO(excel_data)

    df = pd.read_excel(excel_file, engine='openpyxl')
    output = df.loc[:,:]
    print("output", output)
    
    # DataFrame を JSON に変換
    output_json = output.to_json(orient="records")  # レコード形式で変換
    print("output_json", output_json)

    # Lambda のレスポンスとして返す
    return {
        "statusCode": 200,
        "body": json.dumps({"output": json.loads(output_json)})  # JSON を辞書形式にして返す
    }

requirements.txtの変更

railsでいうgemfile的な位置付けのファイルなんですかね。
ここに必要なパッケージを記述します。

requirements.txt
requests
pandas

ビルドしてデプロイする

ここまでで準備は整ったので、ビルドとデプロイをしていきます。

ビルド

$ sam build

デプロイ

$ sam deploy -g --profile ~~~

また、色々と確認されるので答えていく。基本、全てYで良いと思います。
ExcelManipulatorFunction has no authentication. Is this okay? [y/N]: yについては、本来は設定するべきですが、今回は割愛しています。

Configuring SAM deploy
======================

	Looking for config file [samconfig.toml] :  Found
	Reading default arguments  :  Success

	Setting default arguments for 'sam deploy'
	=========================================
	Stack Name [image-sam-app]: 
	AWS Region [us-east-1]: ap-northeast1
	#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
	Confirm changes before deploy [Y/n]: Y
	#SAM needs permission to be able to create roles to connect to the resources in your template
	Allow SAM CLI IAM role creation [Y/n]: Y
	#Preserves the state of previously provisioned resources when an operation fails
	Disable rollback [y/N]: y
	ExcelManipulatorFunction has no authentication. Is this okay? [y/N]: y
	Save arguments to configuration file [Y/n]: y
	SAM configuration file [samconfig.toml]: 
	SAM configuration environment [default]: 

しばらく待って以下のメッセージが表示されたらデプロイ成功です!

Successfully created/updated stack - excel-app in ap-northeast-1

AWSのコンソール画面に確認しにいくと

  • lambda関数
  • API Gateway
    が存在することを確認できました。

試しにcurlで最初に用意したエクセルファイルを送ると、

$ curl -X POST \
     -H "Content-Type: multipart/form-data" \
     -F "file=@Book1.xlsx" \
     https://~~~~~.execute-api.ap-northeast-1.amazonaws.com/Stage/excel_manipulator

こんな感じでエクセルから文字列を読み取って返却されました🎉

{"output": [{"A1": "A2", "B1": "B2", "C1": "C2"}, 
            {"A1": "A3", "B1": "B3", "C1": "C3"}]}

まとめ

振り返ってみるとめちゃくちゃ簡単そう(という簡単)なのですが、

  • Layerを作成してLambdaに設定したらいい
  • dockerを起動して、そこでpip installしたらいい
    などさまざまな情報にあたり、色々試した結果今回の方法に辿り着きました。

同じように詰まっている方の助けになれば幸いです。

参考

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?