2026/05/04

Power BI と M言語:外部API連携のラーニングまとめ

 概要

このドキュメントは、Power BI Desktop と Power BI サービスで外部API(connpass API v2、Doorkeeper API)を使ってデータを取得・加工する際に学んだことをまとめたものです。


1. M言語の基本ルール

変数名のルール

  • 数字から始まる変数名は使えない
    • 90日前 = ...
    • 過去90日 = ...
  • スペースを含む場合は #"..." で囲む
  • 日本語の変数名は使用可能

関数の引数名と外側の変数名の衝突に注意

// ❌ 引数名 subdomain が外側の Query フィールド名と衝突する
GetLatestEvent = (subdomain as text) =>
    Web.Contents(..., [Query=[subdomain = subdomain]])

// ✅ 引数名を sd に変更して衝突を回避
GetLatestEvent = (sd as text) =>
    Web.Contents(..., [Query=[subdomain = sd]])

2. Power BI サービスでの動的クエリの制限

問題

Power BI サービスでは、URLを動的に生成するクエリ(動的データソース)は自動更新できない

解決策:Query=[] オプションを使う

// ❌ 動的URLとみなされ更新不可
ソース = Web.Contents("https://api.example.com/events?" & "subdomain=" & P1)

// ✅ Query=[] オプションを使うことで更新可能
ソース = Web.Contents(
    "https://api.example.com/events/",
    [
        Query=[subdomain = P1, count = "100"]
    ]
)

重要:Text.Combine も動的とみなされる場合がある

ベースURLは固定の文字列にして、パラメーターは Query=[] に渡す。


3. Power BI サービスでの外部API認証設定

問題

ヘッダーにAPIキーを渡す方式の外部APIは、Power BI サービスの資格情報設定で「匿名」認証が拒否される場合がある。

解決策

資格情報設定画面で以下のように設定する:

設定項目
 認証方法                         匿名
 プライバシーレベル                         Public
 テスト接続をスキップする                        ✅ チェックを入れる ← これが重要!

APIキーはヘッダーで渡す

ソース = Json.Document(Web.Contents(
    "https://api.example.com/events/",
    [
        Headers=[#"X-API-Key"= "YOUR_API_KEY"],
        Query=[subdomain = P1, count = "100"]
    ]
))

4. 並列読み込みの無効化

複数のクエリが同時にAPIを叩くと、アクセス制限(429 Too Many Requests)に引っかかる。

設定方法

  1. ファイル」→「オプションと設定」→「オプション
  2. 現在のファイル」→「データの読み込み
  3. テーブルの並列読み込み」→「同時ジョブの最大数
  4. 1(並列読み込みを無効にする)」を選択

5. APIのアクセス制限回避:Function.InvokeAfter

APIに1秒間に1リクエストの制限がある場合、Function.InvokeAfter で待機時間を入れる。

GetLatestEvent = (sd as text) as record =>
    Function.InvokeAfter(
        () =>
            let
                response = try Json.Document(Web.Contents(
                    "https://api.example.com/events/",
                    [
                        Headers=[#"X-API-Key"= "YOUR_API_KEY"],
                        Query=[subdomain = sd, count = "1", order = "2"]
                    ]
                )),
                結果 = if response[HasError] then
                    [最新イベント日 = null, 最新イベントタイトル = "APIエラー: " & response[Error][Message]]
                else if response[Value][results_available] = 0 then
                    [最新イベント日 = null, 最新イベントタイトル = "イベントなし"]
                else
                    [
                        最新イベント日 = response[Value][events]{0}[started_at],
                        最新イベントタイトル = response[Value][events]{0}[title]
                    ]
            in
                結果,
        #duration(0, 0, 0, 1.2)  // 1.2秒待機
    ),

待機時間の目安

待機時間特徴
1.0秒API制限ギリギリ
1.2秒適度な余裕で安全 ✅
1.5秒より安全だが処理時間が長い

6. エラーハンドリング

try...otherwise パターンで途中エラーでも処理を止めない。

response = try Json.Document(Web.Contents(...)),
結果 = if response[HasError] then
    [最新イベント日 = null, 最新イベントタイトル = "APIエラー: " & response[Error][Message]]
else if response[Value][results_available] = 0 then
    [最新イベント日 = null, 最新イベントタイトル = "イベントなし"]
else
    [
        最新イベント日 = response[Value][events]{0}[started_at],
        最新イベントタイトル = response[Value][events]{0}[title]
    ]

7. SubdomainList の管理

複数のクエリで共通して使うサブドメインリストは、別クエリ(SubdomainList)として管理して参照する。

// SubdomainList クエリ
let
    Subdomains = {
        "subdomain-1",
        "subdomain-2",
        "subdomain-3"
        // ...
    },
    クリーン済み = List.Transform(Subdomains, each Text.Clean(Text.Trim(_)))
in
    クリーン済み

注意:Power BI サービス(Dataflow Gen1)での制限

Dataflow Gen1 では他のクエリを参照する「計算されたテーブル」は Premium ライセンスが必要。Power BI Pro では使えない。

解決策:SubdomainList をクエリ内に直接埋め込む。

8. ページング対応

APIの1回の取得件数上限(100件)を超える場合のページング処理。

// 1ページ分を取得する関数
GetPage = (start as number) as list =>
    Json.Document(Web.Contents(
        "https://api.example.com/events/",
        [
            Headers=[#"X-API-Key"= "YOUR_API_KEY"],
            Query=[
                subdomain = P1,
                count = "100",
                start = Number.ToText(start)
            ]
        ]
    ))[events],

// 1回目のAPIコールで総件数を確認
ソース1 = Json.Document(Web.Contents(...)),
総件数 = ソース1[results_available],

// 必要なページ数を計算
ページ数 = Number.RoundUp(総件数 / 100),

// 各ページのstartパラメーター値のリストを生成
Starts = List.Generate(
    () => 1,
    each _ <= (ページ数 - 1) * 100 + 1,
    each _ + 100
),

// 全ページのイベントを取得して1つのリストに結合
全イベント = List.Combine(List.Transform(Starts, GetPage)),

注意

Power BI サービスでは動的な start パラメーターがあるためページングと動的クエリ制限が相性が悪い。100件以内の場合はページングなしで設計し、超えた場合に警告を出す方が現実的。

// 100件超えた場合の警告
件数チェック = if List.Count(SubdomainList) > 100 then
    error "SubdomainList が100件を超えています(現在 " & Number.ToText(List.Count(SubdomainList)) & " 件)。ページング対応が必要です。"
else
    SubdomainList,

9. 複数のAPIソースを結合する

connpass と Doorkeeper など異なるAPIのデータを同じ列名で結合する。

// connpass テーブルと Doorkeeper テーブルを結合
最終テーブル = Table.Combine({connpassテーブル, DoorkeeperTable})

ポイント

  • 列名を統一する(例:acceptedpublic_urlname
  • id など型が異なる場合は Text.From() でテキスト型に統一する

10. Dataflow について

Dataflow の役割

Power BI の Dataflow は ETL(Extract・Transform・Load)に相当する。

ETL内容Dataflow での役割
Extractデータを抽出外部APIからデータを取得
Transformデータを変換列の選択・名前変更・型変換など
Loadデータを読み込むPower BI Desktop にデータを提供

Dataflow Gen1 vs Gen2

Gen1Gen2
Power BI Pro で使える❌(Fabric Premium が必要)
計算されたテーブル❌(Premium必要)
外部API(匿名認証)❌ 制限あり未検証

結論

現時点では Power BI Desktop + Power BI サービス(pbix アップロード) が最も確実。

11. Power BI ダッシュボードのTips

日付の階層化を防ぐ

日付型の列が自動的に年・四半期・月・日に階層化される場合:

方法①:「ファイル」→「オプション」→「データの読み込み」→「自動日付/時刻」のチェックを外す

方法②:M言語で日付を YYYY-MM 形式のテキスト型に変換する

年月 = Text.From(Date.Year(...)) & "-" & Text.PadStart(Text.From(Date.Month(...)), 2, "0")

月の並び順が正しくない場合

テキスト型の 年月 列は文字列順で並ぶため、数値型の並べ替え列を追加する。

年月数値 = Date.Year(...) * 100 + Date.Month(...)

Power BI Desktop の「列ツール」→「列で並べ替え」→「年月数値」を選択。

DAX メジャーの例

// イベント数
イベント数 = COUNTROWS('テーブル名')

// 申込人数合計
申込人数合計 = SUM('テーブル名'[accepted])

// ユニーク支部数(同じ月に同じ支部が複数回開催しても1)
ユニーク支部数 = DISTINCTCOUNT('テーブル名'[subdomain])

12. connpass API v2 メモ

  • エンドポイント:https://connpass.com/api/v2/events/
  • 認証:X-API-Key ヘッダー
  • アクセス制限:1秒間に1リクエスト
  • 1回の最大取得件数:100件
  • 主なパラメーター:subdomaincountorderym

13. Doorkeeper API メモ

  • エンドポイント:https://api.doorkeeper.jp/groups/{group}/events
  • 認証:Authorization: Bearer YOUR_TOKEN ヘッダー
  • 1回の取得件数:25件(ページネーション必要)
  • 主なパラメーター:sortsinceuntilpage

0 コメント:

コメントを投稿

Powered by Blogger.

自己紹介

自分の写真
1989年新卒で日本IBMに入社しダウンサイジング担当としてホストコンピュータと繋げるオフコン、UNIX、PCサーバーのプロジェクトを担当。1997年 MSKK(現日本マイクロソフト)入社、NT4出荷に伴い企業向けサポート部門のビジネスマネージャーとして Excel 使いとなり、2002年 にMSMVPなどをサポートするユーザーコミュ二ティ部門を設立、部門をリード。2006年にMSKK退職後、企業向けのITトレーニング会社・団体に携わり、2014年頃よりPowerBI勉強会主催メンバーの一人として参画、そのコミュニティ活動で MSMVP for Data Platform PowerBI 2017受賞。https://mvp.microsoft.com/ja-jp/PublicProfile/5002635 同年にMVP Awardを返上し、アマゾン ウェブ サービス ジャパンに入社、コミュニティプログラム担当として現在に至る。