Html.Table
は、Power Query M 言語で提供される関数の一つで、HTML コンテンツからテーブル形式のデータを抽出するために使用します。この関数を利用すると、Web ページに含まれるHTML表(<table>
タグ)を解析して、Power Query内で扱えるテーブルデータとして取り込むことができます。
12.1. Html.Table
関数の概要
Html.Table
は、HTML データから指定したパターンに基づいてテーブル形式のデータを抽出するための Power Query 関数です。
Html.Table(
html as any,
columnNameSelectorPairs as list,
optional options as nullable record
) as table
-
html
: HTML コンテンツ(通常はWeb.Contents
で取得)。 -
columnNameSelectorPairs
: 列名と対応する CSS セレクターまたは列データを抽出する式のペアをリスト形式で指定。 -
options
: オプション設定で、RowSelector
を使用して行単位で抽出条件を指定可能。
12.2. CSS セレクターの基本
CSS セレクターは、HTML 内の特定の要素や属性を指定するための方法です。以下は主要なセレクターです。
基本セレクター
名称 | 説明 | 例 |
---|---|---|
全称セレクター | すべての要素 | * |
要素型セレクター | 要素名を指定 | input |
クラスセレクター | class属性を指定 | .classname |
IDセレクター | id属性を指定 | #idname |
属性セレクター | 属性を指定 | [attr] [attr=value] |
属性セレクター
構文 | 説明 | 例 |
---|---|---|
[attr] | その属性を持つ要素 | [name] |
[attr=value] | 指定された属性の値が完全一致 [class="a b c"]は class="a b c"と一致 |
[class="bold-01"] |
[attr~=value] | [class="b"]は class="a b c"と一致 |
[class~="red-02"] |
[attr|=value] | [id|="pre"]は preとpre-viewと一致 |
[class|="red"] |
[attr^=value] | 要素の最初の値に一致 | [class^="bold-02"] |
[attr$=value] | 要素の最後の値に一致 | [class$="red-02"] |
[attr*=value] | [id*="re"]はpreとrenと一致 | [class*="old"] |
[attr operator value i] | 値の大文字と小文字を区別しません | [class$="LISTITEM1" i] |
[attr operator value s] | 値の大文字と小文字を区別します | [id="ListItem1" s] |
組み合わせセレクター
セレクター | 説明 | 例 |
---|---|---|
tag.class |
特定のタグとクラス | div.content |
tag#id |
特定のタグと ID | table#sales |
tag[attr=value] |
属性が特定の値の要素 | a[href="example.com"] |
階層セレクター
名称 | 構文 | 説明 | 例 |
---|---|---|---|
子孫結合子 | A B | 子孫にあたるノード | #MainContent p |
子結合子 | A > B | 直接の子のノード | #MainContent > div > p |
一般兄弟結合子 | A ~ B | Aの後にあるBの要素 | p ~ p |
隣接兄弟結合子 | A + B | Aの直後のBの要素 | span + p |
複数選択 | A, B | AまたはBの要素 | ul, dl |
子要素フィルター
構文 | 説明 | 例 |
---|---|---|
:first-child | 最初の子要素 | #Table-1 > tbody > tr > :first-child |
:first-of-type | セレクタで指定された 最初の子要素 |
#Table-1 > tbody td:first-of-type |
:last-child | 最後の子要素 | #Table-1 > tbody > tr > :last-child |
:last-of-type | セレクタで指定された 最後の子要素 |
#Table-1 > tbody td:first-of-type |
:only-child | 子要素が 1つだけのときに取得 |
|
:only-of-type | セレクタで指定された要素が 1つだけのときに取得 |
|
:nth-child() | 親要素内にあるセレクタで 引数で指定した要素 |
#FirstLetter > span:nth-child(2) |
:nth-of-type() | セレクタと引数で指定された要素 | #FirstLetter > p:nth-of-type(2) |
:nth-last-child() | nth-childを最後から数える | #FirstLetter > :nth-last-child(2) |
:nth-last-of-type() | nth-of-typeを最後から数える | #FirstLetter > span:nth-last-of-type(2) |
12.3. RowSelector
を使用した行単位のデータ抽出
RowSelector
は、複数行のデータ抽出を可能にするオプションです。
12.3.1 単純な table
要素の抽出
以下の例では、CSS セレクターと RowSelector
を使用してウェブページからデータを抽出します。
Product | Price |
---|---|
Widget | $50 |
Gadget | $30 |
上記のテーブルは、tr
で行が区切られているため、以下の様になります。
let
// Htmlコード
Source = "
<table id=""products"">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Widget</td>
<td>$50</td>
</tr>
<tr>
<td>Gadget</td>
<td>$30</td>
</tr>
</tbody>
</table>",
// テーブル作成
ExtractedTable = Html.Table(
Source,
{
{"Product", "td:nth-child(1)"},
{"Price", "td:nth-child(2)"}
},
[RowSelector = "table#products tbody tr"]
)
in
ExtractedTable
12.3.2 複数のtable
要素からの抽出
以下は、2つのtable
要素に分かれているデータを1つのテーブルにします。
Electronics
Phone | $600 |
Laptop | $1200 |
Appliances
Microwave | $200 |
Fridge | $800 |
let
Source = "
<section id=""electronics"">
<h2>Electronics</h2>
<table>
<tr><td>Phone</td><td>$600</td></tr>
<tr><td>Laptop</td><td>$1200</td></tr>
</table>
</section>
<section id=""appliances"">
<h2>Appliances</h2>
<table>
<tr><td>Microwave</td><td>$200</td></tr>
<tr><td>Fridge</td><td>$800</td></tr>
</table>
</section>",
Electronics = Html.Table(
Source,
{
{"Product", "td:nth-child(1)"},
{"Price", "td:nth-child(2)"}
},
[RowSelector = "section#electronics table tr"]
),
Appliances = Html.Table(
Source,
{
{"Product", "td:nth-child(1)"},
{"Price", "td:nth-child(2)"}
},
[RowSelector = "section#appliances table tr"]
),
Result = Table.Combine({Electronics, Appliances})
in
Result
12.3.4 dl
や ul
のリスト要素からの抽出
サンプルページの中で、CSSの修飾でテーブルのように見えているけど、実はリスト要素で作成されているデータも抽出できます。
<div id="route">
<h4>id=rsltlst</h4>
<ul id="rsltlst" class="routeList">
<li>
<dl>
<dt>ルート1</dt>
<dd>
<ul>
<li class="time">19:03→<span class="mark">19:38</span><span class="small">35分</span></li>
<li><span class="mark">726円</span></li>
<li>乗換:<span class="mark">0回</span></li>
</ul>
</dd>
</dl>
</li>
<li>
<dl>
<dt>ルート2</dt>
<dd>
<ul>
<li class="time">19:00→<span class="mark">19:38</span><span class="small">38分</span></li>
<li class="fare"><span class="mark">726円</span></li>
<li class="transfer">乗換:1回</li>
</ul>
</dd>
</dl>
</li>
<li>
<dl>
<dt>ルート3</dt>
<dd>
<ul>
<li class="time">18:58→<span class="mark">19:38</span><span class="small">40分</span></li>
<li class="fare"><span class="mark">726円</span></li>
<li class="transfer">乗換:1回</li>
</ul>
</dd>
</dl>
</li>
</ul>
</div>
let
Source = Web.Contents("https://fukuyori.github.io/RPALT20220117/"),
MakeTable =
Html.Table(
Source,
{
{"ルート","dl > dt"},
{"修正前時刻", "dl > dd > ul > li.time"},
{"所要時間", "dl > dd > ul > li.time > span.small"},
{"金額", "dl > dd > ul > li:nth-child(2)"},
{"乗換", "dl > dd > ul > li:nth-child(3)"}
},
[RowSelector = "ul#rsltlst > li > dl"]
),
// 時刻項目の修正
ItemModification =
Table.AddColumn(
MakeTable,
"時刻",
each Text.Replace([修正前時刻], [所要時間], "")
)
in
ItemModification
最後のステップで、「修正前時刻」に入り込んでいる「所要時間」を消す作業を行っています。
12.3.4 特定の属性を抽出する方法
HTML 要素の属性値を抽出するには、CSS セレクターを指定した後、[Attributes][属性名]
を付加します。
<div id="SideBarLink" class="quicklinks" name="quicklinks">
<div>
<h3>id=SideBarLink</h3>
<h4>id=List-1</h4>
<ul id="List-1" class="list">
<li id="ListItem1" class="ListItemClass"><a href="http://www.google.com/">google</a></li>
<li id="ListItem2" class="ListItemClass"><a href="http://www.yahoo.co.jp/">yahoo</a></li>
<li id="ListItem3" class="ListItemClass"><a href="https://www.bing.com/">bing</a></li>
</ul>
</div>
let
Source = Web.Contents("https://fukuyori.github.io/RPALT20220117/"),
Table =
Html.Table(
Source,
{
{"Name","li > a"},
{"URL", "li > a", each [Attributes][href]?}
},
[RowSelector = "ul#List-1 > li"]
)
in
Table
{"URL", "li > a",each [Attributes][href]?}
の行では、3番目の部分で全ての属性から href
を選び出しています。該当の値が存在しない場合、このステップがエラーとなってしまい、クエリが止まってしまいます。?
をつけておくと、該当の値が存在しない場合には null
が返され、エラーを回避できます。
12.4 ベストプラクティス
1. 行選択の精度を上げる
-
RowSelector
を使用することで、ターゲットとなる行を正確に指定します。これにより、不要なデータを抽出するリスクを軽減できます。
2. セレクターの構造を確認
ブラウザの「要素を検証」機能を活用して、ページの DOM 構造に基づいて適切なセレクターを特定します。
CSSセレクターを適切に指定するためには、以下のツールを活用すると効果的です。
-
CSSセレクターのコピー: HTML構造を確認し、要素を特定するのに役立ちます。
-
セレクターテストツール: CSSセレクターをテストするツールを使用して、正確なセレクターを確認します。
3. 動的ページに注意
- JavaScript によって生成されたコンテンツがある場合、
Web.BrowserContents
を使用する必要がある場合があります。