例を使用してテーブルを抽出する とは

Power BI Desktop で Webページに表示されているテキストを取り込みたい。すでに用意されているコネクターでは TABLEタグを認識しどのテーブルを取り込みますか？になっているのだけど、TABLEタグが使用されているとは限らないし。

Python や R で HTML をパースして取り込むとき、CSS セレクターを使って必要なものを指定する的な機能。2018/05 時点でプレビューの機能だけど、Power Query 以外もちょっと勉強できたので整理しておく。

# やってみる
[データの取得] - [Web]
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/e5817a14-2f82-0728-d460-0bfca919174f.png)

怒られる確率が低いであろうチョイスで [Microsoft Store](https://www.microsoft.com/ja-jp/store/new/games/xbox?category=classics) にお邪魔して。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/18b055a4-9580-a2be-b7c0-b0357731eea3.png)

そのまま読み込めそうな"テーブル"がないのですね。で、[例を使用してテーブルを抽出]する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/c400e960-c206-9e58-f481-ea7218261e07.png)

ここから"例" サンプルを入力していく。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/0caf312d-809a-08a2-3147-f6009b90632e.png)

"タイトル"という列名にしていくつか入力していると、デロンと。グレーの文字は中の人が これでいいよな！って言ってくれているタイトル。もし違っていれば、違っているところを修正すると精度があがる傾向にある。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/ab4c9d53-0ec4-0641-750d-8d53699c0b81.png)

"価格"列を追加して、金額をひとつだけ入力してみた。なんとなくよさげな感じではある。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/de11d8e2-dee2-dba9-3fb7-8d8a73ce5312.png)

しかし、実際読み込んでみるとちょっと修正が必要だったりする。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/fbcdc4d6-5d8b-c2d0-619f-1712d13b5641.png)

デスクトップ アプリケーションだけでここまでできるならイイ感じじゃね？と思うのです。AIですから概ね同じ結果になるのでしょうけど、いつも同じになるとは限らない。ならば、

#もうすこし詳しくみる
一連の UI操作で生成されたPower Query のスクリプトはこんな感じ。
ここから先は人が手助けをすればよいわけで、最初から中の人に頼らず進めてもよいのですけど。

```text:生成されたスクリプト(整形済)
let
    Source = 
        Web.BrowserContents(
            "https://www.microsoft.com/ja-jp/store/new/games/xbox?category=classics"
        ),
    #"Extracted Table From Html" = 
        Html.Table(
            Source, 
            {
                {"タイトル", "H3"}, 
                {"価格", ".c-price"}
            },
             [RowSelector=".m-product-placement-item"]
        ),
    #"Changed Type" = 
        Table.TransformColumnTypes(
            #"Extracted Table From Html",
            {
                {"タイトル", type text}, 
                {"価格", type text}
            }
        )
in
    #"Changed Type"
```

## 利用されている関数
利用されているふたつの関数 Html.Table / Web.BrowserContents あって、これを理解すればよいかなと。Web.BrowserContents も必要になるケースがあるけど、ここでは説明なしで次の機会にでも。
### Html.Table 関数
この関数の構文は、

```text:構文：Html.Table
Html.Table(
    html as any,  // text もしくは binary なので any
    columnNamesSelectorPairs as list,  // 列名とCSSセレクターの組合せ list の list
    optional options as nullable record  // フィールド名 RowSelector の record 値は CSS セレクター
)
```
なので、

```text
Html.Table(
    Source, 
    {
        {"タイトル", "H3"}, 
        {"価格", ".c-price"}
    },
        [RowSelector=".m-product-placement-item"]
)
```
は、クラス属性に "m-product-placement-item" が含まれる要素をまず行(row)とみなし、その子孫の要素のうち、H3タグでマークアップされた文字列を "タイトル"列に、クラス属性に "c-price" が含まれる要素でマークアップされた文字列を "価格"列 にしている。要素を特定するための CSS セレクターのうち、適していそうなものを中の人が考えてくれているわけです。

## "価格"列 だけ見てみる
"タイトル"列はきっと大丈夫として、"価格"列についてはあるべき内容にしてみる。

```html:該当部分のみ(部分的に省略済)
<div class="c-price">
        <span itemprop="price" content="2916">¥2,916</span>
        <span itemprop="priceCurrency" content="JPY/">
        </span>
</div>

<div class="c-price">
        <span itemprop="price" content="2160">¥2,160</span>
        <span itemprop="priceCurrency" content="JPY/">
            <sup>+</sup>
        </span>
</div>

<div class="c-price">
        <s aria-label="定価 ¥1,080 の商品"> ¥1,080

        <span aria-label="割引にて">&nbsp;</span>
        <span itemprop="price" content="270">¥270</span>
        <span itemprop="priceCurrency" content="JPY/">
        </span>
</div>
```
いくつかのパターンが存在していてそれらを網羅できていないわけで、サンプルになるアイテムが少ないからかもしれないけれどもざっくり持ってきている。抽出したいものは"価格"だから、itemprop 属性が price の SPANタグでマークアップされた文字列であろう。

```text:CSSセレクターを修正
Html.Table(
    Source, 
    {
        {"タイトル", "H3"}, 
        {"価格", "SPAN[itemprop='price']"}
    },
        [RowSelector=".m-product-placement-item"]
)
```
これは欲しい結果を得られそうです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193509/7facce75-a84e-d581-c6c7-50d2d5c310b6.png)

```text:ひとまず完成
let
    Source = 
        Web.BrowserContents(
            "https://www.microsoft.com/ja-jp/store/new/games/xbox?category=classics"
        ),
    #"Extracted Table From Html" = 
        Html.Table(
            Source, 
            {
                {"タイトル", "H3"}, 
                {"価格", "SPAN[itemprop='price']"}
            },
             [RowSelector=".m-product-placement-item"]
        ),
    #"Changed Type" = 
        Table.TransformColumnTypes(
            #"Extracted Table From Html",
            {
                {"タイトル", type text}, 
                {"価格", Currency.Type}
            }, 
            "ja-jp"
        )
in
    #"Changed Type"
```

# 参考にした情報
- [Get data from a Web page by providing an example (Preview) - Power BI | Microsoft Docs](https://docs.microsoft.com/en-us/power-bi/desktop-connect-to-web-by-example)
- [CSS セレクター - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Selectors)

# その他
- [Power BI 勉強会 - connpass](https://powerbi.connpass.com/)
- [Power BI Desktop を使い始めるには](http://qiita.com/PowerBIxyz/items/6c4735cb5b5712e70981)