Webスクレイピング超入門① で紹介した IMPORTXML
関数でXPathを開発者ツールによって自動取得する方法がありました。
ただ、この方法にはいくつかの欠点があります。
HTML構造の変更に弱く n 番目の tr
などの指定では上に一つ tr
が追加されたら全て1つズレる、XPathの表記が長すぎてあとから人間が理解しづらいなど。
これらの対策として、できれば「XPathを理解して自分で指定する」のがベストかと思います。(ChatGPTに相談するというのもアリかもしれません)
ということで、XPathの使い方チートシートを置いておくので参考にしつつ開発者ツールの検索機能でいい感じに指定できてるかチェックしてみましょう。
構文 | 説明 | 例 | 結果 |
---|---|---|---|
/ |
ルートノードからの絶対パス | /html/body |
ルートノードからhtml 、その子のbody を選択 |
// |
現在の位置から全ての子孫ノードを検索 | //div |
ドキュメント内の全てのdiv ノードを選択 |
. |
現在のノードを指す | ./title |
現在のノードの子のtitle ノードを選択 |
.. |
親ノードを指す | ../div |
現在のノードの親のdiv ノードを選択 |
@attribute |
属性値を取得 | //a/@href |
全てのa タグのhref 属性を取得 |
* |
任意のタグを指定 | //div/* |
全てのdiv の直下の子ノードを選択 |
[条件] |
条件に一致するノードを選択 | //li[@class='active'] |
class="active" のli タグを選択 |
[n] |
n番目のノードを選択 | //ul/li[1] |
最初のli ノードを選択 |
last() |
最後のノードを選択 | //ul/li[last()] |
最後のli ノードを選択 |
contains() |
部分一致 | //a[contains(@href, 'example')] |
href 属性にexample を含むa タグを選択 |
text() |
ノードのテキストを取得 | //p/text() |
全てのp タグのテキストを取得 |
starts-with() |
文字列が特定の値で始まるノードを選択 | //a[starts-with(@href, 'https')] |
href 属性がhttps で始まるa タグを選択 |
not() |
条件が一致しないノードを選択 | //div[not(@id)] |
id 属性を持たないdiv タグを選択 |
| |
異なる複数のノードを同時に選択 | //h1 | //h2 |
h1 タグとh2 タグを選択 |
@* |
全ての属性を選択 | //img/@* |
全てのimg タグの属性を選択 |
ancestor:: |
特定の祖先ノードを選択 | //li/ancestor::ul |
li タグの祖先にあるul タグを選択 |
descendant:: |
特定の子孫ノードを選択 | //div/descendant::p |
div タグの子孫である全てのp タグを選択 |
following-sibling:: |
特定の後続の兄弟ノードを選択 | //h2/following-sibling::p |
h2 タグの後に続く全ての兄弟p タグを選択 |
preceding-sibling:: |
特定の前の兄弟ノードを選択 | //h2/preceding-sibling::p |
h2 タグの前にある全ての兄弟p タグを選択 |
基本構文
XPathはXMLやHTML文書をツリー構造として扱い、特定の要素や属性を選択するための言語。 以下は基本的な構文です。
/
:絶対パスを指定(ルートノードから始まる)//
:相対パスを指定(文書内のどこからでも検索).
:現在のノード..
:親ノード@
:属性を指定*
:任意の要素や属性を指定text()
:テキストノードを取得
ノード とは1つ1つのタグ(要素)やテキスト(タグの中に書かれている文字)などのこと。
よく使う指定方法
1. 特定の要素を選択
- 例:
<html> <body> <h1>タイトル</h1> <p>段落1</p> <p>段落2</p> </body> </html>
- h1要素を取得:
/html/body/h1
- 取得される要素:
<h1>タイトル</h1>
- 取得される要素:
- すべてのp要素を取得:
//p
- 取得される要素:
<p>段落1</p>
<p>段落2</p>
- 取得される要素:
- 最初のp要素を取得:
(//p)[1]
- 取得される要素:
<p>段落1</p>
- 取得される要素:
2. 属性を指定
- 構文:
要素[@属性='文字列']
- 例:
<div class="content">内容</div> <a href="https://example.com">リンク</a>
- class属性が"content"のdiv要素を取得:
//div[@class='content']
- 取得される要素:
<div class="content">内容</div>
- 取得される要素:
- href属性を持つすべてのa要素を取得:
//a[@href]
- 取得される要素:
<a href="https://example.com">リンク</a>
- 取得される要素:
3. テキストを取得
- 構文:
要素/text()
- 例:
<p>この文章を取得します。</p>
- テキストノードを取得:
//p/text()
- 取得される要素:
この文章を取得します。
- 取得される要素:
※スプレッドシート関数 IMPORTXML
では自動的にテキストを抽出するので指定不要
4. 条件を指定
- 構文:
要素[条件]
- 例:
<ul> <li>アイテム1</li> <li>アイテム2</li> <li>アイテム3</li> </ul>
- 2番目のli要素を取得:
//li[position()=2]
- 取得される要素:
<li>アイテム2</li>
- 取得される要素:
- "アイテム2"を含むli要素を取得:
//li[contains(text(), 'アイテム2')]
- 取得される要素:
<li>アイテム2</li>
- 取得される要素:
応用的な指定方法
1. 部分一致(contains関数)
- 構文:
要素[contains(@属性, '文字列')]
- 例:
<div class="product-item">商品A</div> <div class="product-item">商品B</div>
- class属性に"product"を含む要素を取得:
//div[contains(@class, 'product')]
- 取得される要素:
<div class="product-item">商品A</div>
<div class="product-item">商品B</div>
- 取得される要素:
2. 複数条件の指定
- 構文:
要素[条件1 and 条件2]
- 例:
<div class="item" data-id="123">商品A</div> <div class="item" data-id="456">商品B</div>
- classが"item"かつdata-idが"123"の要素を取得:
//div[@class='item' and @data-id='123']
- 取得される要素:
<div class="item" data-id="123">商品A</div>
- 取得される要素:
3. 子孫ノードの指定
- 構文:
親要素//子孫要素
- 例:
<div class="container"> <span>テキスト1</span> <span>テキスト2</span> </div>
- container内のすべてのspan要素を取得:
//div[@class='container']//span
- 取得される要素:
<span>テキスト1</span>
<span>テキスト2</span>
- 取得される要素:
4. 特定の範囲を指定
- 例:
<table> <tr> <td>1行目1列目</td> <td>1行目2列目</td> </tr> <tr> <td>2行目1列目</td> <td>2行目2列目</td> </tr> </table>
- 2行目の2列目を取得:
//table//tr[2]/td[2]
- 取得される要素:
<td>2行目2列目</td>
- 取得される要素:
親子関係を指定するXPath
XPathでは、親子関係をスラッシュ(/
)やダブルスラッシュ(//
)を使って表現。
直接の親子関係
- 構文:
親要素/子要素
- 例:
<div> <p>テキスト1</p> <p>テキスト2</p> </div>
//div/p
→<div>
の直下にあるすべての<p>
要素を取得。
子孫関係(すべての子要素を含む)
- 構文:
親要素//子孫要素
- 例:
<div> <section> <p>テキスト1</p> </section> </div>
- `//div//p`→ `<div>` のすべての子孫要素の中から `<p>` 要素を取得。
隣接する要素を指定するXPath
XPathでは、隣接する要素を指定するために「兄弟ノード」を扱う軸(sibling axes)を使用します。
直後の兄弟要素(following-sibling)
- 構文:
要素/following-sibling::要素
- 例:
<ul> <li>アイテム1</li> <li>アイテム2</li> <li>アイテム3</li> </ul>
//li[1]/following-sibling::li[1]
→ 最初の<li>
要素の直後にある<li>
要素(「アイテム2」)を取得します。
すべての後続の兄弟要素
- 構文:
要素/following-sibling::*
- 例:
//li[1]/following-sibling::*
→ 最初の<li>
要素以降のすべての兄弟要素を取得します。
直前の兄弟要素(preceding-sibling)
- 構文:
要素/preceding-sibling::要素
- 例:
//li[2]/preceding-sibling::li[1]
→ 2番目の<li>
要素の直前にある<li>
要素(「アイテム1」)を取得します。
親要素を指定するXPath
XPathでは、親要素を指定するために parent
軸を使用します。
親要素の取得
- 構文:
要素/parent::要素
- 例:
<div> <p>テキスト</p> </div>
//p/parent::div
→<p>
要素の親である<div>
要素を取得。
複数条件を組み合わせた指定
XPathでは、親子関係や隣接要素を条件付きで絞り込むことも可能。
例: 特定の条件を持つ兄弟要素
- 構文:
要素/following-sibling::要素[条件]
- 例:
<ul> <li class="active">アイテム1</li> <li>アイテム2</li> <li>アイテム3</li> </ul>
//li[@class='active']/following-sibling::li[1]
→ クラスが「active」の<li>
要素の直後にある<li>
要素(「アイテム2」)を取得。
まとめ
壊れにくいXPathの作成
壊れにくいXPathを作成するには、以下のポイントを意識します。
- 絶対パスより相対パスを優先: HTML構造が変わっても影響を受けにくい。
- 悪い例:
/html/body/div[1]/span
- 良い例:
//div[@class='content']/span
- 悪い例:
- 安定した属性を使用: IDやclassなど、変更されにくい属性を利用。
- 悪い例:
//div[1]
- 良い例:
//div[@id='main']
- 悪い例:
- contains関数を活用: 部分一致で柔軟性を持たせる。
- 悪い例:
//button[1]
- 良い例:
//button[contains(text(), '送信')]
- 悪い例:
idやclassが無くても親子関係の指定でなんとかなるかも
親子関係や隣接する要素を柔軟に指定することができます。
以下のポイントを押さえておくと便利です。
- 親子関係:
/
(直接の子)、//
(すべての子孫) - 隣接要素:
following-sibling
(後続の兄弟)、preceding-sibling
(前の兄弟) - 親要素:
parent
軸を使用 - 条件付き指定:
[条件]
を使って絞り込み
これらを組み合わせることで、複雑なHTML構造から必要な要素を効率的に取得できます。