Azure File ShareにCSVを保存する方法|Python(pandas)でDataFrameを書き戻す実装例

Azure File Share 上のCSVをPython(pandas)で加工し、結果をそのまま同じFile Share内にCSVとして保存したいのに、df.to_csv()だとローカルにしか出力できず困る――そんなときの実装パターンを、最小コードから運用のコツまでまとめます。

目次

やりたいことと、つまずく理由

要件を整理すると次の流れです。

  • Azure File Share に置かれたCSVを読み込む
  • Pythonで pandas.DataFrame(df)として加工する
  • 加工後の結果を、再度 Azure File Share 内に CSVファイルとして保存する

ここで多くの方が最初に書くのが、次のようなコードです。

df.to_csv("output.csv")

しかし df.to_csv("output.csv") は「ローカルパスに書き出す」動作です。Azure File Share はローカルのファイルシステムではないため、単にパスを書いただけではFile Shareに保存されません。File Shareに保存するには、SMBでマウントしてOSに“ローカルドライブ”として見せるか、もしくはAzure Storage File Share SDK(REST API)でアップロードする必要があります。

Azure File ShareにCSVを保存する2つの方法

方法概要向いているケース注意点
SMBでマウントしてローカルパス扱いFile Shareをネットワークドライブとしてマウントし、df.to_csv("Z:/share/output.csv")のように書くWindows Server上で運用していて、SMBマウントが前提の環境実行環境にマウント設定が必要。コンテナやサーバレスでは難しいことが多い
Python SDKでアップロードDataFrameをメモリ上でCSV化し、upload_file()でFile Shareへ書き込むAzure Functions / コンテナ / CIなど、ローカルディスクに依存したくない環境SDKのクライアント階層や上書き・文字コードを理解しておくと安定する

この記事では、より汎用性が高い「Python SDKでアップロード」の方法を中心に解説します。

今回の基本アイデア:メモリにCSVを書き出してからアップロード

ポイントはとてもシンプルです。

  • df.to_csv() の出力先を「ファイルパス」ではなく、メモリ上のバッファ(io.StringIO)にする
  • バッファからCSV文字列を取り出し、bytes(UTF-8)に変換する
  • Azure File Share SDK の upload_file() で、そのバイト列をFile Shareにアップロードする

必要ライブラリと前提

Azure File Share に対してPythonから操作するには、azure-storage-file-share を使うのが定番です。公式ドキュメントでは、必要要件として Python 3.9 以降が挙げられています。

インストールは次の通りです。

pip install azure-storage-file-share pandas

クライアント階層を理解すると迷わない

Azure File Share SDKは、操作対象に応じてクライアントが分かれています。これを押さえると「どこで upload するのか」が一気に明確になります。

クライアント役割よく使うメソッド例
ShareServiceClientストレージアカウント(File service)全体get_share_client() / from_connection_string()
ShareClient特定のファイル共有(share)get_directory_client()
ShareDirectoryClient特定ディレクトリget_file_client() / upload_file()
ShareFileClient特定ファイルdownload_file() / upload_file()

最小実装:directory_client がある前提で Output.csv を作成する

質問の状況(すでに df と directory_client がある前提)なら、次のコードが最小で分かりやすい形です。

import io

# すでに df, directory_client がある前提

# 1. DataFrameをメモリ上の文字列バッファにCSV形式で書き出す
output = io.StringIO()
df.to_csv(output, index=False)  # index=False で行番号は出力しない

# 2. 文字列をバイト列(UTF-8)に変換
csv_bytes = output.getvalue().encode("utf-8")

# 3. 出力先ファイルのクライアントを取得
output_file_name = "Output.csv"
output_file_client = directory_client.get_file_client(output_file_name)

# 4. Azure File Share にアップロード
# 既存ファイルを上書きしたい場合(SDK/環境によっては対応状況に差が出ることがあります)
output_file_client.upload_file(csv_bytes, overwrite=True)

この形にしておけば、ローカルディスクに Output.csv を作らず、生成したCSVデータをそのままAzure File Shareへ保存できます。

なぜ io.StringIO() と encode("utf-8") が効くのか

df.to_csv() は「パスに保存」だけではない

to_csv() は通常「ファイルパス」を渡すとディスクに出力しますが、出力先に“ファイルのように振る舞うオブジェクト”を渡すこともできます。そこで io.StringIO() を使うと、CSVをメモリ上に書き出せます。

StringIOではpandas側のencoding指定が効かない場合がある

pandasの仕様として、path_or_buf に非バイナリのファイルオブジェクト(たとえば StringIO)を渡したとき、to_csv(encoding=...) の指定はサポートされないと明記されています。そのため、自分で .encode("utf-8") して bytes にするのが確実です。

Azure File Shareの upload_file() は bytes/str/stream を受け取れる

Azure File Share SDKの upload_file() は、bytes / str / ストリーム(IO)など複数の形式を受け取れます。さらに encoding の既定値はUTF-8です。ただし運用上は「文字コードを固定したい」「環境差を減らしたい」ことが多いので、今回のように bytes を明示的に渡す形が安全です。

上書きでハマらないための実践パターン

実務で多いのが「同名ファイルを毎日作り直す」「同じファイル名に上書きしたい」といったケースです。ところが、SDKのバージョンや実行環境の違いで overwrite=True がうまく効かない(またはエラーになる)ことがあります。そんなときは、存在確認→削除→アップロードの手順が堅いです。

ファイルがあれば削除してからアップロード(確実)

import io

output = io.StringIO()
df.to_csv(output, index=False)
csv_bytes = output.getvalue().encode("utf-8")

file_name = "Output.csv"
file_client = directory_client.get_file_client(file_name)

# 既存があるなら削除(存在しない場合は何もしない)

if file_client.exists():
file_client.delete_file()

# 新規作成としてアップロード

file_client.upload_file(csv_bytes) 

同じディレクトリ内の削除なら、ShareDirectoryClient.delete_file(file_name=...) を使う選択肢もあります。どちらでも構いませんが、運用コードでは「どのクライアントを持っているか」で書きやすい方を選ぶとよいです。

読み込み→加工→書き戻しまでの完全サンプル

ここからは、File Share上のCSVを読み込み、pandasで加工して、同じFile Shareへ書き戻すまでを一気通貫で示します。ローカルに一時ファイルを作らない構成なので、コンテナやサーバレスでも再現しやすいです。

サンプルコード(接続文字列で認証する例)

import os
import io
import pandas as pd
from azure.storage.fileshare import ShareServiceClient

# 環境変数から接続文字列を取得(コードにベタ書きしない)

CONN_STR = os.environ["AZURE_STORAGE_CONNECTION_STRING"]

# 対象の共有名・ディレクトリ・ファイル名

SHARE_NAME = "myshare"
DIRECTORY_PATH = "data"          # ルート直下なら "" でもOK
INPUT_FILE = "input.csv"
OUTPUT_FILE = "Output.csv"

# 1) サービスクライアント

service = ShareServiceClient.from_connection_string(CONN_STR)

# 2) 共有 → ディレクトリ → ファイル

share_client = service.get_share_client(SHARE_NAME)
directory_client = share_client.get_directory_client(DIRECTORY_PATH)

# 3) File Share上のCSVをダウンロードしてDataFrameへ

input_file_client = directory_client.get_file_client(INPUT_FILE)
download = input_file_client.download_file()
csv_bytes = download.readall()

df = pd.read_csv(io.BytesIO(csv_bytes))

# 4) 加工(例:欠損値を埋め、列名を整える)

df = df.fillna("")
df.columns = [c.strip() for c in df.columns]

# 5) DataFrameをメモリでCSV化

buffer = io.StringIO()
df.to_csv(buffer, index=False)
out_bytes = buffer.getvalue().encode("utf-8")

# 6) 既存があれば削除してからアップロード(安定運用)

out_client = directory_client.get_file_client(OUTPUT_FILE)
if out_client.exists():
out_client.delete_file()

out_client.upload_file(out_bytes) 

認証は接続文字列以外にも、SASトークンや共有キー、Azure AD(azure-identity)など複数の方式があります。運用ポリシーに合わせて選べます。

運用で差がつくポイント

文字コードは「用途で決め打ち」すると事故が減る

CSVの“正解の文字コード”は、利用者と利用ツールで変わります。特に日本語環境では、Excelで開くかどうかで方針が変わるので、チーム内で基準を決めておくのがおすすめです。

文字コード特徴メリット注意点
UTF-8Web/クラウドで標準的多言語に強い。PythonやLinux系で扱いやすいExcelの設定によっては文字化けすることがある
UTF-8-SIG先頭にBOMを付けるUTF-8Excelで開いても文字化けしにくい厳密にBOM非許容の処理系では注意(Web系では稀)
CP932(Shift_JIS系)日本語Windows/Excel互換寄りExcelで開く前提なら安定機種依存文字や絵文字などで表現できない文字がある

UTF-8-SIGで出したい場合は、次のようにBOM付きでエンコードします。

csv_bytes = buffer.getvalue().encode("utf-8-sig")

改行コード(\n / \r\n)を意識すると取り込みが安定する

to_csv() の改行はOS依存(Linuxなら \n、Windowsなら \r\n)になることがあります。外部ツールが改行に敏感なときは、lineterminator を指定して固定すると事故が減ります。

df.to_csv(buffer, index=False, lineterminator="\n")

大きなCSVは「コピー回数」を減らすとメモリが楽になる

StringIO -> getvalue() -> encode() は分かりやすい反面、データ量が大きいと「文字列」「バイト列」の両方を一時的に保持しがちです。サイズが大きい場合は、BytesIO と TextIOWrapper を使って“書いた瞬間にエンコード”する形が有効です。

import io

byte_buf = io.BytesIO()

# TextIOWrapperに書くと、UTF-8にエンコードされた結果がbyte_bufにたまります
text_buf = io.TextIOWrapper(byte_buf, encoding="utf-8", newline="")

df.to_csv(text_buf, index=False)
text_buf.flush()
text_buf.detach()  # wrapperを切り離し(closeでbyte_bufまで閉じないため)

byte_buf.seek(0, io.SEEK_END)
length = byte_buf.tell()
byte_buf.seek(0)

file_client = directory_client.get_file_client("Output.csv")
file_client.upload_file(byte_buf, length=length)

File Shareのファイルは最大1TiBまで扱えるため、パイプライン次第ではかなり大きなCSVも保存できます。ただし、DataFrame化自体がメモリを使うので、処理全体のボトルネック(DataFrameサイズ・変換コスト)も合わせて見直すと効果的です。

認証情報はコードに書かない

接続文字列やアカウントキーをソースコードに直書きすると、漏えい時の影響が大きくなります。少なくとも環境変数、できればKey VaultやマネージドID(Azure AD)など、運用に合った方式に寄せましょう。公式ドキュメントでも、SASや共有キーなど複数のクレデンシャル方式が紹介されています。

よくあるエラーと対処

最後に、Azure File Share×PythonでCSVを扱うときに遭遇しがちなトラブルをまとめます。

症状原因の例対処
アップロード時に「既に存在する」系のエラー同名ファイルが残っている/overwrite指定が効かないexists()で確認し、delete_file()してからアップロードする
404(Not Found)共有名・ディレクトリパスが違う/ディレクトリが未作成Azure Portalでパスを再確認。必要なら事前にディレクトリを作成する
認証エラー接続文字列やSASの期限切れ/権限不足期限・権限(read/write/list)を見直す。運用ではKey VaultやManaged Identityも検討
文字化けExcelでUTF-8を期待通りに解釈していないUTF-8-SIGやCP932に切り替える。どのツールで開くかを決めて統一する

まとめ

Azure File Share内にCSVを作成(保存)するコツは、ローカルパスに書く発想を捨てて、DataFrameをメモリ上でCSV化し、そのままSDKでアップロードすることです。io.StringIO+encode("utf-8") で最小構成を作り、上書きは「削除→アップロード」に寄せると、環境差に強い実装になります。あとは文字コードと改行を“運用で決め打ち”しておけば、日次バッチでもトラブルが減っていきます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次