PythonのGraph API msgraph-sdk
を使ってSharepointの操作をしました。
requests
モジュール使って、RESTでやっている情報は結構あったのですが、msgraph-sdk
で完結させています。
Step
0. 前提
種類 | Version | 備考 |
---|---|---|
OS | Ubuntu22.04.5 LTS | WSL2で動かしています |
Python | 3.11.7 | 少し古いですが、たまたま手元にあった環境 |
Poetry | 2.1.3 |
Python パッケージ
種類 | Version | 備考 |
---|---|---|
azure-identity | 1.21.0 | |
msgraph-sdk | 1.36.0 | |
python-dotenv | 1.0.1 |
その他
- Sharepiotnのサイト作成済
- ローカルのプロジェクトのディレクトリ作成、Poetryの初期設定済
1. Sharepoint権限
こちらの記事の「Azureでのアプリ登録」とまったく同じ方法。なので、当記事では省略します。
わかりやすい記事に感謝です。
2. Python Script
2.0. 前提
環境変数を.env
で設定しています。どの値を参照しているかは「「Azureでのアプリ登録」」に記載されています。
CLIENT_ID=""
TENANT_ID=""
CLIENT_SECRET=""
2.1. パッケージインポート
import os
from pprint import pprint
from azure.identity.aio import ClientSecretCredential
from dotenv import load_dotenv
from msgraph import GraphServiceClient
from msgraph.generated.sites.item.lists.item.items.items_request_builder import ItemsRequestBuilder
2.2. クライアント生成
環境変数を読み込んで、クライアントを生成しています。
load_dotenv(override=True)
CLIENT_ID = os.getenv('CLIENT_ID')
TENANT_ID = os.getenv('TENANT_ID')
CLIENT_SECRET = os.getenv('CLIENT_SECRET')
SCOPES = ['https://graph.microsoft.com/.default']
credential = ClientSecretCredential(TENANT_ID,
CLIENT_ID,
CLIENT_SECRET)
client = GraphServiceClient(credentials=credential, scopes=SCOPES)
2.3. 全サイト取得
全サイトを取得します。これでサイトにどんな情報を持っているか確認します。
※以後もそうですが、ターミナルに出力した結果で値によって消したりするのが手間なので、ターミナル出力結果は基本的に記事に書きません。
sites = await client.sites.get()
pprint(sites)
2.4. サイトをサイト名から取得
サイト名からサイトを取得します。引数をどう設定するのかを調べるのに苦労しました。
HOST
はSharepointにアクセスしたときのホスト名、SITE
はホスト名に続くパスです。
HOST = "<ホスト名>"
SITE = "/sites/functiontest"
site = await client.sites.by_site_id(HOST+":"+SITE).get()
pprint(site)
2.5. ドライブ取得
サイトからドライブを取得します。Shared%20Documents
はDecodeしています。
drives = await client.sites.by_site_id(site.id).drives.get()
drive = next(
(drive for drive in drives.value if drive.web_url == f"https://{HOST}{SITE}/Shared%20Documents"),
None
)
pprint(drive)
2.6. ドライブ内フォルダとファイル取得
ドライブ内のファイルとフォルダを取得しています。
ODataのQuery規則に準じてfilterを設定すべきなのですが、これが効いていません(name eq 'openapi_v2.json'
部分)。filterがないとエラー出るので残しています。
# filterが効いておらず、未調査
query_params = ItemsRequestBuilder.ItemsRequestBuilderGetQueryParameters(
filter = "name eq 'openapi_v2.json'",
)
request_configuration = ItemsRequestBuilder.ItemsRequestBuilderGetRequestConfiguration(
query_parameters = query_params,
)
items = await client.drives.by_drive_id(drive.id).items.\
get(request_configuration)
if items and items.value:
for item in items.value:
if item.folder:
print(f"Folder: {item.name=}, {item.parent_reference.path=}")
else:
print(f"File: {item.name=}, {item.parent_reference.path=}, {item.size=}, {item.file.mime_type}")
2.7. フォルダ内のファイル一覧出力
特定のフォルダ内のファイルを出力しています。
items = await client.drives.by_drive_id(drive.id) \
.items.by_drive_item_id(f"root:/dir01:").children.get()
for item in items.value:
if item.file:
print(f"{item.name=}, {item.parent_reference.path=}, {item.size=}, {item.file.mime_type}")
2.8. ファイルダウンロード
dir02/openapi_v2.json
のファイルをダウンロードして、ローカルに保存させています。
FILE = "openapi_v2.json"
# TextファイルでもBinaryで受け取る
content = await client.drives.by_drive_id(drive.id)\
.items.by_drive_item_id(f"root:/dir02/{FILE}:").content.get()
with open(f"./data/{FILE}", "wb") as f:
f.write(content)
print(content)
2.9. ファイルアップロード
content
はバイナリのファイル内容です。
ファイルはもしSharepointに存在していれば上書きされます。
Sharepointにディレクトリがなかった場合は自動で作成されます。
# ファイルがあったら上書き。フォルダーなかったら作られる
await client.drives.by_drive_id(drive.id) \
.items.by_drive_item_id(f"root:/dir03/{FILE}:").content.put(content)
参考リンク
API アクセス許可についてこちらで確認しました。