👾

Azure Functions + PythonでOpenpyxlを使う

2022/05/22に公開

Microsoft Azure の Azure Functions を Python で作成し、Openpyxlを使って Excelファイルの入出力をおこないます。

ストレージは Azure Blob Storage を使い、Python v12 SDKを使って操作します。

この記事の前提条件

  • アクティブなサブスクリプションが含まれる Azureアカウント がある
  • Azureに ストレージアカウント が作成済である
  • ChromeBook で開発する
  • ChromeBook には VSCode がインストールされている
  • HttpトリガーのFunctionが作成済である

SDKを使用して Azure Blob Storage を操作する方法は、次の記事を参考にしてください。
https://zenn.dev/gatabutsu/articles/d31eba3bcd31f5

パッケージのインストール

local.settings.jsonの編集

Pythonの外部ライブラリを使用するためlocal.settings.json"PYTHON_ENABLE_WORKER_EXTENSIONS": "1"を追加します。

local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "<Blob Storageへの接続文字列>",
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "PYTHON_ENABLE_WORKER_EXTENSIONS": "1"
  }
}

openpyxlのインストール

ローカル環境にopenpyxlをインストールします。

VSCodeのターミナルで対象プロジェクトのディレクトリに行き、次のコマンドでインストールします。

pip install openpyxl

requirements.txtに追加

インストールした「openpyxl」を依存パッケージの一覧であるrequirements.txtにも追記します。バージョンはローカル環境にインストールしたものに合わせます。
Azureにデプロイする際にシステムによってインストールされます。

requirements.txt
azure-functions
azure-storage-blob
openpyxl == 3.0.9

Python関数の編集と実行

Httpトリガーで起動された関数が、Azure Blob Storage 上のExcelファイルを読み取り、既存シートの最下行のセルに値を書き込むというプログラムを書いていきます。Excelファイルが存在していなかった場合には、新たにファイルを作成して新しいシートを追加した上で、値を書き込みます。

プログラムの全体像

  1. Azure Blob Storage に接続し、BLOBを操作するために BlobClient オブジェクトへの参照を取得します
  2. 対象となるExcelファイルが存在している場合は、一旦ダウンロードしてopenpyxlで読み込みます
  3. Excelファイルが存在しない場合には、新たにopenpyxlのオブジェクトを用意します
  4. 「datasheet」というシートの最下行のセルに値を書き込みます
  5. 一時ファイルを作成し、編集したopenpyxlのオブジェクトをExcelファイルとして保存します
  6. 保存した一時ファイルを Azure Blob Storage にアップロードします
__init__.py
import logging
import azure.functions as func
import os
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient, __version__
from tempfile import NamedTemporaryFile

import openpyxl

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    # Blob Storage へ出力するファイルの準備
    # 出力するExcelファイルは仮にsample.xlsxとします
    connect_str = os.environ['AzureWebJobsStorage']
    blob_service_client = BlobServiceClient.from_connection_string(connect_str)
    blob_client = blob_service_client.get_blob_client(container='container/sdk-sample', blob='sample.xlsx')

    if blob_client.exists():
        # ファイルが存在している場合はいったんダウンロードしてopenpyxlで読み込みます
        with NamedTemporaryFile('wb',suffix='.xlsx') as xltmp:
            xltmp.write(blob_client.download_blob().readall())
            wb = openpyxl.load_workbook(xltmp.name)
            # datasheetシートに書き込みます        
            ws = wb["datasheet"]
            maxrow = ws.max_row
    else:
        # ファイルが存在しない場合はExcelブックのオブジェクトを作成します
        wb = openpyxl.Workbook()
        wb.create_sheet(title="datasheet")
        ws = wb["datasheet"]
        maxrow = 0
 
    ws.cell(row=maxrow+1,column=1).value = "Hello"
    ws.cell(row=maxrow+2,column=2).value = "world."
    
    # 一時ファイルを作成しExcelブックとして保存します
    with NamedTemporaryFile('wb',delete=False) as tmp:
        wb.save(tmp.name)

    # 一時ファイルをいったんクローズしたあと再度オープンし、Blob Storageにアップロードします
    # その際、ファイルは上書きします
    with open(tmp.name, "rb") as data:
        blob_client.upload_blob(data,overwrite=True)        

    # 一時ファイルを削除します
    os.remove(tmp.name)
    
    return func.HttpResponse(f"ok.")

1.BlobClient オブジェクトへの参照を取得する

環境変数「AzureWebJobsStorage」から Azure Blob Storage への接続文字列を取得します。ローカルでの開発時にはlocal.settings.jsonの値が参照され、Azure上でサービスを実行する場合には、Azure Functionsのアプリケーション設定に設定されている同キーの値が参照されます。

その接続文字列を使って Azure Storage リソースと BLOB コンテナーを操作を操作するためのBlobServiceClientを用意し、さらにBLOB を操作するためのBlobClientオブジェクトへの参照を取得します。

ここでは、予め Azure Blob Storage 上に、コンテナ「container」、ディレクトリ「sdk-sample」を作成しておくものとします。

BlobClientオブジェクトのexists()メソッドで、Blob Storage上に対象のExcelファイルが存在しているかの判定をします。

connect_str = os.environ['AzureWebJobsStorage']
blob_service_client = BlobServiceClient.from_connection_string(connect_str)
blob_client = blob_service_client.get_blob_client(container='container/sdk-sample', blob='sample.xlsx')

if blob_client.exists():

2.Excelファイルが存在している場合は、一旦ダウンロードしてからopenpyxlで読む

Excelファイルが存在していた場合は、BlobClientオブジェクトのdownload_blob()メソッドを使って、BLOBをダウンロードしそれを一時ファイルに書き込みます。

openpyxlで読み込めるようにするため、一時ファイルは拡張子として「.xlsx」を指定して作成します。

ファイルの新規作成時に「datasheet」というシートを作成してあるので、そのワークシートを指定してmax_rowでデータの入力されている最下行を取得しておきます。

with NamedTemporaryFile('wb',suffix='.xlsx') as xltmp:
    xltmp.write(blob_client.download_blob().readall())
    wb = openpyxl.load_workbook(xltmp.name)
    ws = wb["datasheet"]
    maxrow = ws.max_row

3.Excelファイルが存在しない場合は、openpyxlを用意する

Blob Storage上に対象のExcelファイルが存在していなかったらopenpyxlで新たにワークブックとシートを作成します。

今回は、新しくワークブックを作成したときにデフォルトで作成されるシート「Sheet」は無視して、「datasheet」というシートを追加します。

wb = openpyxl.Workbook()
wb.create_sheet(title="datasheet")
ws = wb["datasheet"]
maxrow = 0

4.Excelを編集し、作成した一時ファイルに保存する

対象のシート最下行のセルに値を書き込みます。openpyxlsave()メソッドはファイル名を指定する必要があるので、新たに一時ファイルを作成してその名前を渡します。

あとでアップロードするので自動的に削除はしないでおきます。

with NamedTemporaryFile('wb',delete=False) as tmp:
    wb.save(tmp.name)

5.編集後のファイルをアップロードする

openpyxlで保存した編集後のExcel一時ファイルを、再度オープンしてBlobClientオブジェクトのupload_blob()メソッドを使ってBLOBをアップロードします。

with open(tmp.name, "rb") as data:
    blob_client.upload_blob(data,overwrite=True)

実行結果

関数を実行すると1度目の実行で Azure Blob Storage に sample.xlsxが新規作成されます。2度目の実行で、1度目に作成した「datasheet」の最終行の次のセルから、同じ値が書き込まれます。

1度目の実行


2度目の実行

まとめ

本記事では Python で作成した Azure Functions で、Openpyxl を使って、Azure Blob StorageでExcelファイルを入出力する方法を紹介しました。

Blob Storage での入出力に気をつければ、あとはOpenpyxlの世界の話になるのでOpenpyxlでできることの範囲でExcelを操作できます。

Azure上でデータベースのデータをExcelに出力したり、Application InsightsのデータをExcelで加工することなどにも使えます。

'なんでもExcel'には賛否あると思いますが、Excelは非エンジニアでも使えるので、必要なデータをExcelに出力することで、部門間や役割を超えて、情報の共有がしやすくなるのではないかと思います。

参考

https://zenn.dev/shohei_aio/articles/a155ddfc8c9cab

Discussion