ラベル Power BI の投稿を表示しています。 すべての投稿を表示
ラベル Power BI の投稿を表示しています。 すべての投稿を表示

2026/05/04

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

Power BI M言語・API連携 学習メモ

概要

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


1. M言語の基本ルール

変数名のルール

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

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

M言語
// ❌ 引数名 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=[] オプションを使う

M言語
// ❌ 動的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キーはヘッダーで渡す

M言語
ソース = 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 で待機時間を入れる。

M言語
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 パターンで途中エラーでも処理を止めない。

M言語
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)として管理して参照する。

M言語
// 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件)を超える場合のページング処理。

M言語
// 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件以内の場合はページングなしで設計し、超えた場合に警告を出す方が現実的。

M言語
// 100件超えた場合の警告
件数チェック = if List.Count(SubdomainList) > 100 then
    error "SubdomainList が100件を超えています(現在 " & Number.ToText(List.Count(SubdomainList)) & " 件)。ページング対応が必要です。"
else
    SubdomainList,
⚠️ connpass API v2 のページング不具合(2026年5月時点)

connpass API v2 では results_available および start パラメーターが正しく動作しない不具合が確認されています。

ドキュメントの定義では results_returned は「含まれる検索結果の件数」、results_available は「検索結果の総件数」とされています。

実際の動作(不具合):

  • count=100, start=1 の場合、results_available は総件数ではなく count と同じ 100 を返す
  • count=10, start=1 の場合も同様に results_available は 10 を返す
  • count=100, start=101 の場合、results_returned は次の100件ではなく 0 を返す
  • count=10, start=2 の場合、results_returned は全体の2件目から10件ではなく、count=10 で絞った結果の2件目から9件目となり 9 を返す

本質的な問題: count パラメーターが「取得件数」ではなく「検索結果のフィルター件数」として機能しており、start パラメーターがその絞られた結果に対して適用されているため、ページングが根本的に機能しない状態です。

API v1 との比較: v1 では count=1 の場合でも results_available に総件数(例:91)が返されており正常にページングが機能していました。v1・v2 ともにドキュメントの定義は同じであるため、v2 の明確な不具合と判断されます。

現実的な対応策: 各サブドメインの年間イベント数が100件以内であれば現状のコードで全件取得可能です。100件を超える場合は connpass の修正を待つか、別の取得方法を検討する必要があります。

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

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

M言語
// 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 形式のテキスト型に変換する

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

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

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

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

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

DAX メジャーの例

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
  • ⚠️ ページング不具合あり(2026年5月時点)results_available が総件数ではなく count と同じ値を返す。詳細はセクション8を参照。

13. Doorkeeper API メモ

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

2023/03/12

[Power Query] for ループのような処理を Power Query で行う方法

connpass や Doorkeeper の REST API を利用してIT勉強会のデータを取得して、Excel であれば JSON データを表形式のテーブルに展開し Pivot テーブルに取りこんで分析する、Power BI であれば、ファクトテーブルとして取りこんでレポート、ダッシュボード作成に利用する、などを過去のブログでも紹介しています。

その中で既に使っているやり方なんですが、意外にこの方法で悩んでいる方もいらっしゃるので、まとめて説明してみます。おおまかな手順は以下です。

1) APIを使ったデータ取得をパラメータを受け取って処理する関数にする
2) 渡したいパラメータ(for の 1 から n に相当)の要素をもったテーブルを作る
3) 要素をパラメータとして関数に渡し、結果をとる
4) エラーが発生している行を削除する
5) 関数の結果からピボットテーブルを利用する

ここから connpass の場合を例にとって説明します。Doorkeeper でも考え方は同じです。

1) APIを使ったデータ取得をパラメータを受け取って処理する関数にする

connpass の API の説明ページは以下で、そこでデータ取得のためのサンプルや、各種パラメータの説明があります。

connpass API リファレンス

https://connpass.com/api/v1/event/?keyword=JAWS のように keyward= に検索したいワードを入れることでレスポンスを JSON 形式で受け取ることができます。connpass の場合はレスポンスのデータ件数の初期値が10なので10件しか出力されないのですが、このcount パラメータは最大100に設定でき、かつ、初期値1の開始位置パラメータの start を設定することで 1-100、その次は 101-200、さらにその次は 201-300 と出力を受け取ることができます。URLは以下のようなものを生成できればいいわけです。

1-100件目を取得
https://connpass.com/api/v1/event/?keyword=JAWS&count=100&start=1
101-200件目を取得
https://connpass.com/api/v1/event/?keyword=JAWS&count=100&start=101
201-300件目を取得
https://connpass.com/api/v1/event/?keyword=JAWS&count=100&start=202

キーワードやcountは固定、start パラメータを上述のように変更してリクエストしすべての結果を貰って処理します。

以下は上述の 1-100件目を取得のURLを使って、connpass から JSON データを取得し、ワークシートにテーブルとして展開する手順です。VBAなどのコードを書かずにダイアログ設定だけで JSON をワークシートのテーブルとして展開できることがわかると思います。

[データ]タブの[データの取得と変換](これが Power Query) グループの [Webから] を選択して、URL の入力ボックスに上述のURLをコピーする。

Power Queryエディタが立ち上がって、API リクエストの結果、受け取ったオブジェクトがアイコンで表示されるので、ここで右クリックメニューから [JSON] を選ぶ。

レスポンス情報の「まとめ」が表示される。ここでわかるのは 1番目から開始して、100件を返信として受け取って、可能な返信の件数は 1,135件ある、ということです。JSONで受け取った100件分は、events の List にはいっているので、[List] をクリックします。

Listが展開されて [Record] となりますが、この Record をクリックしてしまうと1件分だけが展開されます。これを全件展開してたいので、リボンの左はしにある [テーブルへの変換] をクリックします。

[テーブルへの変換] 確認のダイアログが表示されるので、そのまま [OK] 

リスト形式のものが「テーブル形式」に変換されます。これによる赤丸の「展開」ボタンで、すべての行にある「レコード」が展開されます。

ここでレコードに含まれる内容が確認できます。このまま OK で構いませんが、[元の列名をプレフィックスとして使用します] のチェックをオフにして、column1 というプレフィックスを消した方が冗長な列名にならずにすみます。

100件分がテーブルとして展開されます。


リボンの [閉じて読み込む] をクリックすると、Powery Query を閉じて、ワークシート上に JSON のレスポンス結果をテーブルに変換したデータが作成されます。

これが基本形です。
この URL の start に受け渡す部分をパラメータ化して変更できる「カスタム関数」を作成します。

いくつかのやり方があるようです。ここでは以下のように「詳細エディター」で Powery Query の M言語を直接いぢります。

Powery Query エディターが閉じられている状態なら、Excel のリボンの [データ] タブの [クエリと接続] グループの [クエリと接続] をクリックし、クエリと接続 ペインを表示して、先ほど作成したクエリをダブルクリックして Power Query エディターを起動します。


[ホーム]タブの [クエリ]グループの [詳細エディタ-] をクリックします。

p1という文字列型のパラメータを宣言する以下の一部を let の前に入れます。URLの一部になるのでデータの属性は text (文字列) とします。

(p1 as text) =>

そして、URLの部分の start= の直後をダブルコーテーションで閉じて、&p1 を追加します。

ソース = Json.Document(Web.Contents("https://connpass.com/api/v1/event/?keyword=JAWS&count=100&start="&p1)),

[完了]ボタンで詳細エディターを終了します。

これでこのクエリはパラメーター入力が必要なクエリ関数になりました。
名前もわかりやすいものに変えておきましょう。右側の[クエリの設定]ペインの名前プロパティで変更可能です。

2) 渡したいパラメータ(for の 1 から n に相当)の要素をもったテーブルを作る

この作成した関数を使って、1,101、201、301、、、、とパラメータを入力して、すべてのデータをひとつのテーブルすると Excel 上では分析するデータソースとして使いやすいです。ワークシートに展開すると 100万行の制限がありますが、Pivotテーブルで利用するデータソースとして使う場合は、必ずしもデータをワークシートに接続する必要はなく、接続情報として保存することで Pivot テーブルのソースとして使えます。

さて、APIでリクエストをして何件あるかはそのとき時でかわります。ただ、上記で API を投げたとき、少なくとも現時点では 1,135 件あることは分かっているので、1,101,201,,,で、1401 とか、1901 まであればカバーできそうなのはわかっています。

そこで 1, 101, 201, 301... 1901 のパラメータリストを作り、そこのデータをパラメータとして関数に渡して、それぞれ100件分のデータを取りこむ、という手順を Power Query を使って踏んでいます。

まず、[データ]タブの[データの取得と変換]グループにある[データの取得]から、空のクリエを選択します。

19個のパラメータを作成するので、入力ボックスに ={1..19} と入力します。すると、1から19までの数字がはいったリストが作成されます。

リボンのテーブルへ変換をして、リストフィールドの数値を使って 1, 101, 201 を作る数式を使った追加の列を作ります。数式は n*100-99 を使ってみます。
テーブル変換した後で、[列の追加]タブの[カスタム列]を選択します。新しい列名はパラメータ変数名の p1 にして、使用できる列 Column1 をダブルクリックすると カスタム列の式に代入されます。そこに *100-99 を追記して [OK] をクリックします。


パラメータはテキストを渡すので、p1列名の横の [ABC123] ボタンを押して、属性をテキストに変換します。

3) 要素をパラメータとして関数に渡し、結果をとる
4) エラーが発生している行を削除する

ここからがカスタム関数を作った醍醐味です。[列の追加]タブの[カスタム関数の呼び出し]をクリックして、ダイアログを開きます。列名はそのままで、関数クエリのドロップダウンでは作成したカスタム関数が表示されるので、それを選びます。
関数を選ぶと、その関数のパラメータに何をいれるかを指定できるドロップダウンリストがでます。列名として指定した p1 をパラメータとして渡す設定をします。

すると関数名の列が新しく追加されます。

11行以降がデータが無いためErrorになるので、この行を削除します。追加列を選択した状態で、[ホーム]タブの[行の削除]から[エラーの削除]を選択します。

削除が終わったら、列名の左にある展開ボタンをクリックして、100件分のデータの展開をします。列名が長くなるので、[元の列名をプレフィックスとして使用します]のチェックを外し、[OK]をクリックします。(以降のサンプルの図は Column1 を外していない図になります。すみません)

各列の属性を適切なものにします。特に集計をしたい列は[10進数]とする、日付の列は [日付/時刻/タイムゾーン]を選んでから、[日付]を選び、[列タイプの変更]ダイアログで [新規手順の追加] を選択して、Excel が理解できる形式に変更してください。


最後は不要な列を削除します。(必要な列だけ選んで他の列を削除するという操作)

[閉じて読み込む]をクリックすると、Power Queryエディタを閉じて、結果をワークシートに展開します。1136行(=データは1,135)で、最初に確認したAPIの返答の result_available の数と一致していることがわかります。

5) 関数の結果からピボットテーブルを利用する 

ここまでくれば、あとは Pivotテーブルで集計できます。
すでにテーブル形式になっているので、[挿入]-[ピボットテーブル]で、この手順通りであれば [クエリ1]というテーブルが指定されている状態でピボットテーブルを作成します。
connpassの場合だと、started_at が開催日、accepted が申し込み人数で、Power Queryで日付データを Excel がわかる形式に変換しているので、以下のような Pivotテーブルが十数秒でできあがります。titleをカウントして何回の勉強会・イベントが開催されているかを集計しています。

この集計方法の最強にして最大の長所は「更新」できることです。一度、このピボットテーブルを作成してしまえば、[データ]タブの[すべて更新]をクリックすることで、再度 connpass に対して REST API を投げて、最新のデータを取得します。その間、Excel 下部のエリアに以下のような「バックグラウンド クエリを実行しています」メッセージが表示されます。

このメッセージが消えたら、ピボットテーブル上で右クリックメニューを表示して、そこから [更新] を選ぶことで、ピボットテーブルも最新になります。

注意すべき点

正確な集計にするためには keyword で取得した後のデータに「ゴミ」が混じっていないかのチェックを入念にしています。実際、この方法で取得したデータのテーブルはファクトテーブルであって、ある意味履歴のようなものです。対象とすべきデータなのかどうかのチェックはスタースキーマであればディメンジョンテーブル(マスターテーブル)と付き合わせるので、ピボットテーブル分析にいく手前で別のステップを踏んで、分析用のデータソーステーブルを作成します。[中止] などの言葉がタイトルにあれば省く、などの例外処理も気がついたら入れていくので、クエリは一度作ったら終わりではなく、常にデータの信頼性を担保するためのチェックをします。このケースの場合は、厳格な命名規則が適用されているわけではないので、毎週、毎月のように新規に追加されたデータから、クエリが正しいかを確認する作業をしています。

一方で、ゴミの有無だけではなく、データの取りこぼしが発生することも確認しています。理由ははっきりとわかっていませんが、一度取りこぼしたデータは次回の更新時にとってくることはないため、一次的なものというより keyword クエリにかからない connpass 側のデータの持ち方かと思います。この場合は強制的に event_id を指定すると詳細を取りこむことができるので、対象となるURL/event_idで強制取り込みリストを作り、そこを参照させて個別にデータを取りこむようにする工夫をしています。

過去の投稿と被っているところも多々ありますが、何かしらの参考になれば幸いです。

2022/04/19

[Power BI] カスタム関数をつかったQueryを含むデータセットが自動更新できない

(追記: Excel のブックをそのまま Power BI Service にあげて使用するのではなく、Power Query + Excel Pivot Table レポートを Power BI Desktop でレポートを書き換えています。Power Query クエリの多くをそのまま移行できるのは、モダンExcel + Power BI の組み合わせの使い勝手の良いところです)

データを元にしたレポートやダッシュボードを複数のユーザーと定期的に共有することは、Excel で工夫・時間をかけて実現できますが、Power BI Service を含む Power BI は、その目的のためにつくられたこともあり、Excel での苦労や時間を軽減してくれます。

特に、モダンExcel な使い方をしているユーザーにとって魅力に見えるのは、レポートのデータ更新の自動化 につきるかと思います。

以前紹介した投稿 [Excel] Excel で JSON データを読み込む で利用してる connpass を使ったレポートなども、個人でデータを確認する分には Excel は柔軟で使いやすいのですが、他の人と、定期的に共有するとなった場合の共有方法については非常に悩みました。Excel ユーザーとしては OneDrive の活用が理想だったと思いますが、OneDrive と PowerQuery (取得と変換)の組み合わせはあまり良くありません。OneDrive といえども他社のファイルサーバー/サービス同様にxlsxのファイル保存先としてしか使えず、メールやメッセンジャーなどで保存先のパスを共有しながら、画像を貼り付けるような使い方になってしまいます。

もし、クラウドに保存している xlsx の「データ更新」ボタンが定期的、自動的に押され、そこにあるデータを表示できたら、、、

これは Pivot Table レポートや Power Query を活用し、複数の人とそれらを共有しなければならないデータエンジニアがいつも思うことです。これを可能にするのが、Power BI Service のダッシュボードやレポートで、データセットの設定にある「スケジュールされている更新」です。


この設定を行い、レポートおよびダッシュボードを共有することで、自動更新された最新データの表示が可能になります。以下は17日、18日の設定時に発生したエラーを含む対応がうまくいき、データセットが自動更新された履歴です。成功すると [状態] が完了になります。


解決しなければならなかった課題は複数あったのですが、今回、関連する日本語の情報を見つけることがなかなかできなかったので、そのうちのひとつを今回ご紹介したいと思います。残りもおって紹介したいと思います。

エラーメッセージ

データセットの[更新スケジュールの設定] で表示されたエラーの内容は「現在、以下のデータソースが更新をサポートしていないため、このデータセットの更新をスケジュールできません、、、」「・Query1 のデータソース」でした。


Query1 は具体的なクエリの名前を指しているわけではありません。
さらに具体的な Query に含まれるエラーの原因を探します。上の図の青い文字のリンク「データセットの表示」をクリックすると、エラーが発生しているデータセットの更新の状況を確認することができ、[最新の情報に更新済み] に更新しようとした日時が表示され、その横に注意マークがあるのでそれをクリックしてみました。するとどうやらデータセットに含まれる「動的データソース」があるために更新されない、と明示的に書かれていました。


このデータセットには、動的データソースが含まれています。動的データソースは Power BI サービスでは更新されないため、このデータセットは更新されません。詳細情報: https://aka.ms/dyanmic-data-sources。のメッセージ画面

このダイアログで紹介している https://aka.ms/dynamic-data-sources のページを開くと、実は非常に詳しく Power BI におけるデータ更新について書かれているのですが、これを全部読み込んで理解するのはなかなかつらいものがあります。以下、このページの日本語の URL です。

Power BI でのデータの更新
https://docs.microsoft.com/ja-jp/power-bi/connect-data/refresh-data

今回のケースにおける問題となった動的データソースは、connpass から REST API を使い JSON データを取りこむ際につかった start パラメーターが原因でした。どうしてこれが原因であるかがわかったのは後述する YouTube 動画のおかげです。

    ソース = Json.Document(Web.Contents("https://connpass.com/api/v1/event/?keyword=JAWS&count=100&start=" & p1)),
しかし、この start パラメーターによるページングの機能を使って、対象とするデータを Power BI に取りこむ(import) ことをしなければ集計ができません。この時点で日本語の情報が少なかったので、Power BI の UI を英語に切り替えて、英語でエラーメッセージやダイアログのメッセージを拾って検索することにしました。

ヘルプは上級者も含むユーザーを対象としているため、解決につながる直接的なイメージをその段階では思い浮かべることができなかったかもしれませんが、今思うとこの段階で Web.Contents のヘルプを読み込むべきでした。以下のヘルプの 例1 が解決方法だったのです。

Web.Contents
https://docs.microsoft.com/ja-jp/powerquery-m/web-contents 

解決方法は動的データソース表記を変更する

英語のキーワードのよる検索の試行錯誤の結果、以下の YouTube の解説ビデオを見つけることができました。

Making sure your Power BI web data source refreshes online (RelativePath, Query, BrowserContents)

上記の YouTube をみて、それから Web.Contents のヘルプをみると ? をつかったパラメーターの表記は NG で、ダイナミックURLを受け付けない静的ベース URL に対して、RelativePath や Query プロパティをつかった方法を使えばいいことがわかります。

上述のソースは以下のような表記方法に変わりました。
    ソース = Json.Document(Web.Contents("https://connpass.com/api/v1/event/?keyword=JAWS&count=100",
        [Query=[start=p1]])),

複数のパラメータの表記方法などは Web.Contens のヘルプに書かれています。
これで、パラメーターの変数を変えたいために動的URLを使うカスタム関数を作成し、その関数がクエリに含まれるためにスケジュールした更新が失敗する、といったことを回避することができます。

以上、参考になれば幸いです。

2020/05/01

[Excel データの取得と変換] Webページのテキストデータを取得する

[注意] Webサイトによってはスクレイピングを明確に禁止しているサイトもあるので、利用の際は確認してください。

Power Query / Power BI の[データの取得と変換]によりさまざまなデータソースから Excel や Power BI Desktop にデータを取り込むことができることは、このブログでもご紹介してきました。

特に「テーブル」を使った Web ページからのデータの取り込みは非常に簡単になったのも [データの取得と変換] を使う理由です。この素晴らしい機能が、いまだに Excel ユーザーの中で知名度が低いのは、本当に残念です。

[マイクロソフト] チュートリアル:Power BI Desktop を使用して Web ページのデータを分析する
https://docs.microsoft.com/ja-jp/power-bi/desktop-tutorial-importing-and-analyzing-data-from-a-web-page

今回は、HTMLのテーブルにもなっていないWebページのデータを取得する方法をご紹介します。俗に「ウェブ スクレイピング」と呼ばれるもので、そもそもデータの再利用などを考慮していないページから、最新のデータを取得する、昔はやった方法です。

Doorkeeperconnpassで勉強会開催情報を公開しているAWSのユーザーグループ「JAWS-UG」の両サービスの登録会員数の取得を例としてその方法をご紹介します。ちなみに両サイトはREST APIを公開しているので、勉強会開催情報の取得では本来スクレイピングする必要はないのですが、公開APIを調べる限りだと「登録メンバー数」を取得するAPIがありませんでした。しばらく「登録メンバー数」の取得は月1回なので目視&手作業で行っていましたが、外出自粛で時間ができたので「データ取得の自動化」をしようと思い立ちました。APIを使ったデータの取り込みは以下でご紹介しました。

[Excel] Excel で JSON データを読み込む
https://road2cloudoffice.blogspot.com/2017/06/excel-excel-json.html

Webスクレイピングのポイントは「テキスト ファイル」で扱う

操作の大まかな流れは以下になります。

  1. Webページのhtmlファイルを「テキスト ファイル」としてPower Queryに取り込む
  2. 該当する行を固有のキーワードでフィルターをかけて特定する
  3. 特定した行から、該当するデータを抽出する
  4. 抽出したテキスト データの属性を調整する(テキストから整数など)
それでは順を追ってみていきましょう。

1)テキストファイル形式で取り込む

Webページの構成上「テーブル」形式のものは取り込みやすいのですが、以下のような数値を取り込むのは、HTMLの構造上、テーブル内のデータとして認識されていません。


このようなWebページからデータを取り込むには、まずリボンの[データ]タブの[データの取得と変換]の[データの取得]にある、[その他のデータ ソースから]の[Webから]を使います。

(直接 [Webから] でも OK。ただしExcel2016以前の[Webクエリ]は別モノなので注意)

サンプルとして利用する、JAWS-UG全国のURL(https://jaws-ug.doorkeeper.jp/)を入れます。今回の場合は [基本] を選択したままで [OK] を押します。


今回 [Document] のテーブルビューは使えません。WebページをHTMLのテーブルビューではなく「テキスト」として扱います。そのため、URLのフォルダーアイコン上で右クリックメニューの [データの変換] を選びます。


Power Query エディター ウィンドウが開くので、[クエリの設定] の [適用したステップ] にある [ソース] の横にある歯車マーク(①)をクリックして、[形式を指定してファイルを開く] のドロップダウンリスト(②)を開き、[テキスト ファイル] (③)を選択します。


テキスト ファイル形式を指定することで、Power Query内にWebページのhtmlファイルは「テキスト」として読み込まれます。ここから該当する箇所を探します。

2)フィルターをかけて行を特定する

上からテキストを見て探してもよいですが、最終的には「フィルター」の機能を使って、該当するデータを含む行を絞り込みます。今回の場合は、メンバー数のデータがある部分を取り出したく、該当する記述は以下になります。

<a class="label label-full community-members-count" href="/members">9464人</a>
class名の"label label-full community-members-count"と href="/members" はユニークでフィルターのキーワードで使えそうです。以下のようにタグをコピーして行フィルターをかけます。

3)行からデータを抽出する

MID関数のような機能を使ってテキストの行からメンバー数に該当する数値を抜き出します。抜き出した結果は列を追加して新しく加えるので、[列の追加] タブにある [抽出] を使います。

4)データの属性を調整する

抽出されたデータは「テキスト」なので、これを整数に変換し、列名も変更します。
以下のように簡単に変更することが可能です。


上記では Doorkeeper のWebページからメンバー数を抽出しましたが、同じ手順で connpass からもメンバー数を抽出することが可能です。いくつかの支部のメンバー数を抽出した結果は以下のようになります。


一度シートを作成してしまえば、あとは [データ] タブの [クエリと接続] の [すべての更新] ボタンで、Webページを参照し、最新のデータに更新します。以下のアニメーションGIFでは、合計が 22,887人だったものが、東京支部が1名増えて、22,888人に更新されています。


なお、今回はスクレイピングの手法で、これからも再利用可能なWebページでしたが、Webページの作り方によっては再利用ができないものもあるので、すべてのケースに適用できるわけではないことをご留意ください。(毎回手作業が発生するものは自動化する意味がないからです。ここでいう「自動化」は [クエリと接続] と [すべて更新] ボタンを押すことで最新のデータになるものを指します)

次のステップ

この手の自動化ツールは汎用性を目指さなくてもいいので、該当する支部をすべて抜き出しシートにコピペしながら手作業で一度作成すれば、あとは [すべて更新] で瞬時に最新データに更新されるので、自動化・省力化の目的は達成です。

しかし、ちょっと冗長な感じがするので、時間があれば以下のようなことを検討できるかもしれません。

  • URLを変数としてもらい、メンバー数を返すユーザー関数にする
  • ユーザー関数を使って、計算対象支部の増減に柔軟に対応するシートにする
たぶん、できると思います。対象支部は50以上あるので、汎用化するか、手作業でやるかは微妙な数ですが、できれば汎用化して、またアウトプットしたいと思います。

もうひとつは「最終開催日」の取得です。これはREST APIの勉強会情報の開催日からも取れそうですし、ページを指定して上記の方法のようなスクレイピングでもとれそうです。

以下、ポエム

しばらくこのデータの取得と更新は月に1回、対象の支部が50程度なので、1時間くらいかけて行っていました。各支部の状況なども確認できるので、決して無駄な時間ではない、と考えていましたが、このデータのチェックを月1回から週1回に行う可能性が出てきました。

そうなると、週1回の1時間かかるデータ更新は外部の協力者に作業依頼する方法もありました。しかし、私たちはIT業界にいます。データを扱うITベンダーのマーケティング部門の人間であれば、ITを使って自動化・省力化を考えたほうが前向きだと思っています。ノンテク部門はお金で解決しがちですが、IT業界にいるのですから、やはりITで解決することにまずはチャレンジしたいです。

立場上、できれば、AWSのGlueやQuickSightを使ったほうがいいと思います。個人でデータを扱うのに適した Excel ではなく、クラウド上でデータ処理を行い、他の人たちと処理したデータを共有すべきタスクができたらチャレンジしたいと考えます。

以上、最後はポエムでまとめてしまいましたが、REST APIなどが提供されていない場合、昔ながらのスクレイピングによるデータ取得も、現在、Excel や Power BI Desktop の Power Query でコーディングレスで可能なことがおわかりいただけたかと思います。
何らかの参考になれば幸いです。

2018/06/13

[Excel 取得と変換] カンマで区切られた複数回答結果を集計する

ネット上でアンケートを作成して、スマホやパソコンで答えてもらい、その結果をCSVなどで受け取るサービスってよくありますよね。身近な(でも、意外に使われていない)サービスとしては、Excel Onlineのアンケート機能(最近は Excel Survey という名称のようですが)、なんてものがあります。([追記 2022.3] Excel Survey はサービス終了し、Forms へ統合されました)

ラジオボタンやドロップダウンリストで複数の選択肢から1個だけ選択させる、という設問は、結果が1つだけなので、1セルに1つの値が入りますが、複数回答可能な設問になると、1つのセルに複数の値が入ります。アンケートの結果として手に入れた表が以下のようになります。
セル内でカンマで区切られた複数回答の例
これも「集計・分析しづらい表」の代表例とも言えます。今回はこの複数回答結果のような、1セルにカンマなどの区切り文字で複数値が含まれているデータを簡単に Power Query (Excel であれば取得と変換)で集計する方法をご紹介します。ワークシート関数を駆使しなくても、VBAを使わなくても、Excelの新しい機能である「取得と変換」(Power Query)を知っていれば対応可能です。
上記の例の場合、ビール、ウィスキー、ワインが選ばれた数を知りたい、そこから、東京でビールを選んだ人はどのくらいいるのか、性別で見た時に、、、という分析をしたいわけですよね。こういう分析は、やはりピボットテーブルが使いやすいと思います。

セルの中の区切り文字でセルを分割する

Power Query - 取得と変換を使わなくても、[データ]タブの[データ ツール]グループにある「区切り位置」の機能を使えば、カンマ区切りの1セルのデータを複数セルに分割することができるのは確かです。

話は脱線しますが、これまでも同じ事ができてるんだから、あえて「取得と変換」 Power Query を使う必要はない、と考えてしまいがちですが、Power BI と Excel の製品動向から考えると、早めに同じ機能なのであれば「取得と変換」の使い方に慣れたほうが得策だと感じています。メリットやデメリットなどいろいろ〇×表で書けると思いますが、一番の違いは、元のデータが Excel のワークシート上に無くてもよい、という点、よって元のデータを直接加工しないで済む、という点だと思います。

実際の手順を追ってみましょう。

1) 元のデータをテーブルにする
モダンエクセルの基本は表形式のデータはテーブルにする、です。Ctrl+Tでテーブル形式に変換してしまいます。

2) テーブルからクエリ エディターを開く
変換したテーブルを元データとして、この元データを加工するためにクエリ エディターを開きます。アクティブセルをテーブル内において、[データ]タブの[取得と変換]グループにある[テーブルから]をクリックして、クエリ エディターを立ち上げます。

3) 列を指定して区切り文字による列分割を行う
サンプルデータであれば「好きな飲み物(複数回答可能)」の列を選択(ヘッダーをクリックでOK)し、区切り文字に「カンマ」を選択して列の分割を行います。手順は以下のアニメーションGIFを参考にしてください。
クエリ エディターの [変換] - [列の分割]機能を使って、カンマ区切り文字で列を分割する
集計・分析しやすいデータの持ち方に変換する

上記の列の分割までは「区切り位置」機能と大差ありませんが、取得と変換を使ってほしいのは、この後の「列ピボットの解除」の処理があるからです。
[追記] 列の分割機能の詳細設定オプションで「分割数」を「行」にすることで、以下の列ピボット解除を行わずに、一気に「好きな飲み物列」の各行にデータの展開が可能です。最後にその手順のアニメーションGIFを追加しました。ご指摘ありがとうございます!

データを区切ったまではいいのですが、まだこのデータは分析には向いていません。ビールやウィスキー、ワインといったデータは「好きな飲み物」の列にタテに入ることで、ピボットテーブルを使った分析が可能になります。

この横に並んでいるデータを縦にするのが「列のピボット解除」の機能です。すでにこの機能は以前の投稿で何度も紹介しています。クロス集計表・マトリクス表でデータが提供されてしまうことが多いExcel界隈では、本当にこの機能を知っている、この機能の使い方を熟知しているか、そうでないかで大きな違いがでると思います。

以下、その手順をおったアニメーションGIFです。
列ピボットの解除
ピボットテーブル レポート機能を使って集計する

ここまでデータの整形ができれば、あとはピボットテーブル レポートを使って集計が可能になります。

まずはビール、ウィスキー、ワインそれぞれがいくつ選ばれているかを集計します。もとのデータは1セルにカンマ区切りで入っていたデータを、列分割と列ピボット解除でテーブル形式にしたデータに整形し、ピボットテーブル レポートにします。

以下が、上記のアニメーションGIFの終わりの状態(クエリ エディターで列ピボット解除の状態)からピボットテーブル レポート作成までをアニメーションGIFにしたものです。
ピボットテーブル レポートの作成
あとは、ピボットテーブル レポートの機能を使ってクロス集計による分析が可能になります。地域毎や性別などで、どんな飲み物が選択されたか集計できます。アニメーションGIFの最後のほうではウィスキーを選んだデータのドリルダウンを行っています。ピボットテーブル レポートだからこそできる分析機能ですね。
ピボットテーブル レポートによる集計とドリルダウン
ここまで「一行も」関数を使った数式やVBAのコードを書いていません。

まとめると、データの取得と変換、Power Queryに代表される「ETL機能」を Excel で使うべき理由は、極端なことをいえば「ピボットテーブルで分析しやすいデータを作る」ことかもしれません。集計や分析でピボットテーブルを使いこなす人にとっては無くてはならないものです。

Power Query(取得と変換)とピボットテーブルはセットで覚えてしまうのをお勧めします。

皆さんの業務の参考になれば幸いです。
[追記] 列のピボット解除ではなく、列の分割の詳細オプションで行に展開した場合のアニメーションGIFが以下です。こちらのほうが簡単!
なお、このオプション名の「分割数」ですが、英語版は「Split into」で「分割先」です。このオプション、最初は分割先として列しかなく、最大数を指定していた記憶があり、途中で分割先として「行」が追加されたと思います。その名残りでしょう(要は更新し忘れているのでしょう)

列の分割の詳細オプションを使って、それぞれの行に展開する

[追記 2022/2] カンマで区切られた複数回答を複数行にして行を増やしたくない、以下のような結果がほしい、という参照記事を見かけました。上のやり方にもうちょっとだけ手を加えると自分の好きな集計結果にできるので追記します。

上記のサンプルを例にすると、以下のような表がほしいようです。
ビール   3
ウィスキー 4
ワイン   4

またはそこからビールを選んだ人だけの表
ビール Aさん
ビール Bさん
ビール Cさん

これは「集計結果の表」で、考え方としては本ブログでそれそれの行に展開したデータ(テーブル)をデーターソースとして、関数やフィルター機能などを使って集計するのですが、ピボットテーブルさえ覚えてしまえば、このような集計はあっという間にできます。
PowerQuery(取得と変換)でデータソースを整理して、集計はピボットテーブルで処理するのがモダン Excel の呼吸 壱の型みたいな鉄板だと思います。
以下ではデータソースとなるテーブルから「ピボットテーブル」を追加し、飲み物でまとめたレポートを作成します。そしてビールの行の数字をダブルクリックして、ビールを選んだ人の表を作成しています。

Power Query を使ってデータベースにある大量のデータを処理してレポート作成をする業務になればなるほど、Excel のセル関数を使う場面は少なくなり、データソースであるテーブルにフィルターをかけて「生データを眺める」こともデータ量が多いので現実やらなくなります。逆に SUMIFS 関数など知らなくてもいいので、ピボットテーブルさえできれば、集計は可能ですし、教育コスト(習得まで時間がかかる)が高いVBAを学ばなくても、必要な表をピボットテーブル上でダブルクリック(ドリルダウンと呼びます)で一瞬で作成できます。

暴論ですが、(元データが比較的整い、綺麗なデータを扱うことができる)ビジネスパーソンは関数やVBAをやらなくてもいいので、Power Query とピボットテーブルの機能をマスターすると、かなりデータ抽出、集計およびレポートの時短が可能になり、間違いも少なくなると思っています。

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を返上し、アマゾン ウェブ サービス ジャパンに入社、コミュニティプログラム担当として現在に至る。