📈
Microsoft Graph APIからExcelを操作しよう
2024-12-04
約4832字
この記事は、Microsoft 365 Advent Calendar 2024 5日目の記事です。
今年とある案件でExcelをゴリゴリいじる機会がありました。
これまでExcel自体もあまり触ってこなかった私が、その案件を通じてMicrosoft Graph APIの操作を調べたので、その知見をまとめようと思います。
Microsoft Graph APIとは、Microsoft 365 のさまざまなデータやサービスを操作できるREAT API です。
今回の開発要件としては、PythonスクリプトからExcelを加工するものでした。
そのため、Microsoft Graph APIを使う以外にも、OpenPyXLのようなOSSのライブラリを使う方法もあり、当初はそちらを採用していました。
しかし、実際に使っているうちにさまざまな制約に悩まされました。
例えば、Excel内にスライサーの機能を使っている場合、OpenPyXLのPythonスクリプトを実行後に以下のWarningが出て、情報が失われてしまいます。
/Users/xxx/.pyenv/versions/3.11.7/lib/python3.11/site-packages/pylightxl/pylightxl.py:260: UserWarning: pylightxl - Ill formatted workbook.xml. Skipping NamedRange not containing sheet reference (ex: "Sheet1!A1"): スライサー_xxx - #N/A warnings.warn(msg, UserWarning)
これでは、開発要件を満たさないので、OpenPyXLの採用は見送りMicrosoft Graph APIを使い加工することになりました。
ただし、Microsoft Graph APIは、Microsoft 365 business standardの契約が必要で、私の場合、そのためにプランをアップグレードしました。
Microsorg Graph APIを使うためには、各種IDとクレデンシャルが必要になります。以下の手順で取得しましょう。
ここで取得した「テナントID」「クライアントID」「シークレットの値」を使い、以降のAPIにアクセスします。
PythonからREST APIを投げて、OneDrive上のExcelに対して基本的な操作を行います
まず、「テナントID」「クライアントID」「シークレットの値」から「アクセストークン」を取得します。
import requests
tenant_id = "xxx"
client_id = "xxx"
client_secret = "xxx"
authority_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
scope = ["https://graph.microsoft.com/.default"]
token_data = {"grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret, "scope": " ".join(scope)}
token_r = requests.post(authority_url, data=token_data)
token = token_r.json().get("access_token")
次にuser_id を取得します。
endpoint = f"https://graph.microsoft.com/v1.0/users"
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
response = requests.get(endpoint, headers=headers)
for user in response.json().get("value"):
print(user.get("id"))
user_id = response.json().get("value")[0].get("id")
OneDriveのルートフォルダ直下に sample.xlsx
を配置する処理が以下になります。
4MBを超えるファイルをアップロードする際は、セッションアップロードを使います。
upload_file_name = "sample.xlsx"
endpoint_prefix = f"https://graph.microsoft.com/v1.0/users/{user_id}"
upload_session_url = f"{endpoint_prefix}/drive/root:/{upload_file_name}:/createUploadSession"
data = {"item": {"@microsoft.graph.conflictBehavior": "rename", "name": upload_file_name}}
response = requests.post(upload_session_url, headers=headers, json=data)
upload_url = response.json().get("uploadUrl")
with open(upload_file_name, "rb") as f:
file_data = f.read()
upload_headers = {"Authorization": f"Bearer {token}", "Content-Range": f"bytes 0-{len(file_data)-1}/{len(file_data)}"}
response = requests.put(upload_url, headers=upload_headers, data=file_data)
sample
というシート名のA1からC5の範囲の値(1行目がName, Age, Cityなテーブル)を更新する場合、以下のようになります。
ちなみに、一度に大量のデータ(数千行レベル)をPOSTすると504になることがあるので、分割してリクエストを投げるなどする必要があります。
import json
range_address = "A1:C5"
sheet_name = "test"
endpoint_prefix = f"https://graph.microsoft.com/v1.0/users/{user_id}"
url = f"{endpoint_prefix}/drive/root:/{upload_file_name}:/workbook/worksheets('{sheet_name}')/range(address='{range_address}')"
data = {
"values": [
["Name", "Age", "City"],
["Nagashima", 88, "Tokyo"],
["Ochiai", 70, "Nagoya"],
["Kudo", 61, "Fukuoka"],
["Miura", 50, "Yokohama"],
["Shinjo", 52, "Sapporo"]
]
}
response = requests.patch(url, headers=headers, data=json.dumps(data))
response.raise_for_status()
上記のシートの値を更新した後に、既存のピボットテーブルを更新する場合は、以下のような処理になります。
url = f"{endpoint_prefix}/drive/root:/{upload_file_name}:/workbook/worksheets"
response = requests.get(url, headers=headers)
for sheet in response.json().get("value"):
url = f"{endpoint_prefix}/drive/root:/{upload_file_name}:/workbook/worksheets/{sheet.get('name')}/pivotTables/refreshAll"
response = requests.post(url, headers=headers, json={})
Microsoft OneDriveのマイドライブ直下に、 sample.xlsx
がありそれをローカルにダウンロードする操作は以下。
filename = "sample.xlsx"
endpoint_prefix = f"https://graph.microsoft.com/v1.0/users/{user_id}"
download_url = f"{endpoint_prefix}/drive/root:/{filename}:/content"
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
response = requests.get(download_url, headers=headers)
with open(filename, "wb") as f:
f.write(response.content)
今回は、Microsoft Graph APIを使ってExcelを操作する方法を紹介しました。
自分が開発する際に、意外とピンポイントで情報がまとまっている記事がなかったので、実装にとても苦労しました。
仮に、同様のことで困っている方がいれば参考になれば良いかと思います。
©︎ 2025 - Yard