クローラ作成に必須！XPATHの記法まとめ

最近クローラーを作成する機会が多く、その時にXPathが改めて便利だと思ったので
XPathについてまとめてみました！
XPathを学ぶ方の役に立てれば幸いです。

# 初級編
## XPathとは
XPathはXML文章中の要素、属性値などを指定するための言語です。
XPathではXML文章をツリーとして捉えることで、要素や属性の位置を指定することができます。
HTMLもXMLの一種とみなすことができるため、XPathを使ってHTML文章中の要素を指定することができます。

例えば、

```html
<html>
...
  <body>
    <h1>ワンピース</h1>
    <div class="item">
      <span class="brand">iQON</span>
      <span class="regular_price">1,200円</span>
      <span class="sale_price">1,000円</span>
    </div>
  </body>
</html>
```

このようなHTMLの場合であれば、ざっくりと下記のようなツリー構造に表すことができます。

![b25b0fb2-a476-5d64-f4a0-973ef29ac215.png](https://qiita-image-store.s3.amazonaws.com/0/63241/e5b1cde4-66ef-ac05-783f-7572d080ee62.png)

XPathはこのようなツリー構造から要素を取得します。


## XPathの基礎
### ロケーションパス
XPathは、ロケーションパスによって表されます。
ロケーションパスとは、ツリー構造から特定の要素を指定するための式のことです。
ロケーションパスは、URLのように『/』で要素を繋げて書きます。

```html
<html>
...
  <body>
    <h1>ワンピース</h1>
    <div class="item">
      <span class="brand">iQON</span>
      <span class="regular_price">1,200円</span>
      <span class="sale_price">1,000円</span>
    </div>
  </body>
</html>
```
このHTMLにおいて『h1要素』を取得するXPathは、
ツリー構造の上から順に『html要素→body要素→h1要素』と指定します。
ロケーションパスで表すと、

```
/html/body/h1
```
このようなXPathになります。

![xpath1.png](https://qiita-image-store.s3.amazonaws.com/0/63241/f2dc747a-5699-845b-308c-c62f159b7d7b.png)


# 中級編
## 属性について
classのような要素に紐づく属性をXPathでは『@』で表します。
『1,200円』という要素を取得したい場合は、属性を用い下記のように書くことができます。

```
/html/body/div/span[@class='regular_price']
```

## //を用いて途中までのパスを省略

```
/html/body/div/span[@class='regular_price']
```
このXpathを『//』を用いて、ノードパスを省略することができます。
『//』は、descendant-or-selfの省略形です。
すなわち起点となるノードの子孫すべての集合を表します。
例えば、

```
/html/body/div/span[@class='regular_price']
```
このXPathを『//』を用いて省略すると、下記のように書くことができます。

```
//span[@class='regular_price']
```



## 指定する文字列が含まれている要素を取得する: contains
containsは、指定する文字列が含まれている要素を取得します。

```html
<img class="large_image">
<img class="small_image">
<img class="thumbnail">
```
上記のHTMLからclassにimageがつくものをすべて取得したい場合、『contains』を用いることができます。
contains関数は、第1引数文字列内に、第2引数文字列が含まれているかどうかを調べる関数です。
classにimageがつく要素すべて取得する、という条件をcontainsを用いて表すと下記のような書き方になります。

```
//img[contains(@class, 'image')]
```
このXPathは、classにimageを含むimg要素を取得するという意味になります。


![2.png](https://qiita-image-store.s3.amazonaws.com/0/63241/24b4321e-266e-8f38-61ce-a2465d308549.png)


またテキスト中に含まれている文字を指定したい場合には、text()とcontainsを組み合わせます。

```html
<div class="item">
  <h1>ワンピース</h1>
  <div class="price">1,200円</div>
    <div class="description">
	   冬に最適なニットワンピースです。
	   品番:100000000
    </div>
  </div>
</div>
```
このHTMLから『品番』という文字を含んでいる要素を指定したい場合は、

```
//div[contains(text(), '品番')]
```
と書くことができます。


さらに、『指定する文字を含むJavaScript』を取得する場合は、下記のように書くことができます。

```
//script[contains(text(), 'stock')]
```

## 要素の位置を指定: position
要素の位置を指定したい場合はpositionを使用します。
positionは、指定したノードから何番目のノードかを指定することができます。

```html

<ul>
  <li>色を選択</li>
  <li>ホワイト</li>
  <li>レッド</li>
  <li>ブルー</li>
</ul>
```
このHTMLでposition()を使ってみます。
### position() =
上記のHTMLで『レッド』はli要素の３番目のなのでpositionを用いて

```
//li[position()=3]
```
と表すことができます。
またposition()=3を省略し

```
//li[3]
```
と書くこともできます。

### position() > 
『色を選択』以外のli要素を取得する場合はpositionを用いて下記のように表すことができます。

```
//li[position()>1]
```
『色を選択』はli要素の１番目であるため、position()>1は、『色を選択』以外のli要素を指定します。

![position.png](https://qiita-image-store.s3.amazonaws.com/0/63241/e13aff20-c797-f77e-beeb-0824e1409b5e.png)



## テキストノードの取得
要素内のテキストを取得したい場合は、『text()』というテキストノードを用います。

```html
<p>Sサイズ <span>レッド</span></p>
```

このHTMLから『Sサイズ』という文字列のみを取得したい場合は、text()を用いて

```
//p/text()
```
と書くことができます。


## not
notは述部にて否定を表します。

```html
<img src="http://sample.ne.jp/sample_main_image.jpg">
<img src="http://sample.ne.jp/sample_sub_image.jpg">
<img src="http://sample.ne.jp/sample_thumbnail.jpg">
```
このHTMLから`http://sample.ne.jp/sample_main_image.jpg`以外の
@srcを取得したい場合はnotを用いて

```
//img[not(contains(@src, 'main'))]/@src
```
と書くことができます。


## or
or条件をXPathで使うことができます。

```html
<img src="http://sample.ne.jp/sample_100.jpg">
<img src="http://sample.ne.jp/sample_200.jpg">
<img src="http://sample.ne.jp/sample_300.jpg">
<img src="http://sample.ne.jp/sample_400.jpg">
<img src="http://sample.ne.jp/sample_500.jpg">
<img src="http://sample.ne.jp/sample_600.jpg">

```

このHTMLから、100か300を含むsrcを取得したい場合は、orを用いて下記のように書くことができます。

```
//td[contains(@src,'100') or contains(@src, '300')]
```

また、100か300以外のsrcを取得したい場合は、notとorを組み合わせます。

```
//td[not(contains(@src,'100') or contains(@src, '300'))]
```


## and
and条件も、XPathで使うことができます。

```html
<img src="http://sample.ne.jp/main_100.jpg">
<img src="http://sample.ne.jp/main_300.jpg">
<img src="http://sample.ne.jp/sub_100.jpg">
<img src="http://sample.ne.jp/sub_300.jpg">
<img src="http://sample.ne.jp/thumbnail_100.jpg">
<img src="http://sample.ne.jp/thumbnail_300.jpg">
```
このHTMLから、『main』と『300』を含むsrcを取得したい場合は、　andを使って下記のように書くことができます。

```
//img[contains(@src, 'main') and contains(@src, '300')]
```

# 上級編
## 軸・ノードテスト・述部
ロケーションパス内で要素を表現する際、『軸・ノードテスト・述部』と呼ばれるものを用いて表現します。

| 名前 | 説明 |
|--------|--------|
|軸|ツリー上の位置関係を指定する|
|ノードテスト|選択するノードの型と名前を指定する|
|述部|選択するノードの集合を、任意の式を使用してさらに細かく指定する|

```
/html/body/h1
```
というXPathはノードテストのみで要素を表していました。
ノードテストだけでは欲しい要素を取得できない場合は、軸や述部を使用することで細かく要素を指定することができます。


## 述部について
ツリー図に、classなどの属性の情報を加えたものが下記の図になります。

![node.png](https://qiita-image-store.s3.amazonaws.com/0/63241/6db909ab-f7a2-5495-2b18-1afb1795df1f.png)



## 軸
軸は、ツリー上の位置関係を指定するものです。
軸の代表的なものとして、以下のような種類があります。

| 名前 | 説明 |
|--------|--------|
|self|ノード自身を表す|
|child|ノードの子ノードの集合|
|parent|ノードの親ノードの集合|
|ancestor|ノードから祖先ノードの集合(親も含む)|
|descendant|ノードから子孫ノード集合|
|following|ノードの後に出てくるノードの集合|
|preceding|ノードの前に出てくるノードの集合|
|following-sibling|ノードと同じ階層にあり、かつ後に出てくる兄弟ノードの集合|
|preceding-sibling|ノードと同じ階層にあり、かつ前に出てくる兄弟ノードの集合|


軸を先ほどまでの図に加えてみます。
どこを起点として考えるかによって位置関係は変わりますが、今回は『div』を中心に軸を考えます。

![ziku.png](https://qiita-image-store.s3.amazonaws.com/0/63241/d1a4d444-6ca1-f1ca-e411-02c733377491.png)


今回はdivを中心に考えるため、div自身は起点となるノードつまり『self』になります。
bodyは、divから見て一つ上の階層つまり親となるので『parent』、　3つのspanは、一つ下の階層つまり子となるので『child』となります。
また、h1要素は、divと同じ階層かつdivより前に出現するので、軸は『preceding-sibling』となります。

### childの省略について
明示的に軸を指定しない場合は、軸がchildとみなされます。
そのため基本的にchildは省略することができます。

### //を用いて途中までのパスを省略
『//』は、descendant-or-selfの省略形です。
すなわち起点となるノードの子孫すべての集合を表します。
この『//』を用いて、パスを省略することができます。

例えば、

```
/html/body/div/span[@class='regular_price']
```
このXPathを『//』を用いて省略すると、下記のように書くことができます。

```
//span[@class='regular_price']
```


## 軸::ノードテスト[述語]
軸・ノードテスト・述部を用いてXPathを書く場合、『軸::ノードテスト[述部]』という書き方で要素を指定します。


```html
<html>
...
  <body>
    <h1>ワンピース</h1>
    <div class="item">
      <span class="brand">iQON</span>
      <span class="regular_price">1,200円</span>
      <span class="sale_price">1,000円</span>
    </div>
  </body>
</html>
```
このHTMLから『1,200円』の要素を取り出すXPathは下記のように書くことができます。
`/html/body/div/span[@class='regular_price']/self::text()`

![zyutugo.png](https://qiita-image-store.s3.amazonaws.com/0/63241/21a33254-1443-158d-5db1-eec2da16a331.png)



また、省略した形で書くと、こうなります。

```
//span[@class='regular_price']
```

ここまでざっくりと軸・ノードテスト・述部を説明しました。
軸・ノードテスト・述部についてもっと詳しく知りたい方は、下記のページがすごくわかりやすいのでぜひ参考にしてみてください。
http://www.techscore.com/tech/XML/XPath/XPath3/xpath03.html/

## 指定された要素より後の兄弟要素を持ってくる: following-sibling::
軸のところで紹介しましたが『following-sibling::』は、起点となるノードと同じ階層にあり、かつ起点となるノードより『後』に出てくる兄弟ノードの集合を表す軸です。
この『following-sibling::』は、テーブル要素を指定するときに大活躍します。

```html

<table>
  <tr>
    <td>生産国</td>
    <td>日本</td>
  </tr>
  <tr>
    <td>素材</td>
    <td>綿</td>
  </tr>
</table> 
```
このようなテーブルが用意されている場合、『綿』をどのように取得したらよいでしょうか。

```
//td[4]
``` 
と書くこともできますが、td要素が増えたり減ったり変化がある場合、`//td[4]`の指定する要素が変わり対応できません。

そこで、『following-sibling::』を使います。
綿を取得したい場合は、

```
//td[contains(*, '素材')]/following-sibling::td[1]
```
と書くことができます。

![following.png](https://qiita-image-store.s3.amazonaws.com/0/63241/632c303f-7996-23b5-a6f2-f73247ab4318.png)



## 指定された要素より前の兄弟要素を持ってくる: preceding-sibling::
『preceding-sibling::』はfollowing-sibling::の対となる軸で、起点となるノードと同じ階層にあり、かつ起点となるノードより『前』に出てくる兄弟ノードの集合を表す軸です。
こちらもテーブル要素を指定するときに大活躍します。

```html

<table>
  <tr>
    <td class="title">22cm</td>
    <td class="title">23cm</td>
    <td class="title">24cm</td>
  </tr>
  <tr>
    <td class="title">レッド</td>
    <td class="inventory">在庫あり</td>
    <td class="inventory">在庫あり</td>
  </tr>
  <tr>
    <td class="title">ブルー</td>
    <td class="inventory">在庫あり</td>
    <td class="inventory">在庫なし</td>
  </tr>
  <tr>
    <td class='title'>グリーン</td>
    <td class='inventory'>在庫あり</td>
    <td class='inventory'>在庫あり</td>
  </tr>
</table>
```
上記のようなHTMLにおいて、色（レッド, ブルー, グリーン)をすべて取得したい場合、どのようなXPathで取得できるでしょうか。
単に、

```
//td[@class='title']
```
では色だけでなくサイズも取得してしまいます。
このようなときに、preceding-siblings::を用います。

```
//td[@class="inventory"][1]/preceding-sibling::td
```
このように書くことで、class属性がinventoryのtd要素の１番目(td[@class='inventory'][1])の1つ前の要素（preceding-sibling::td)すなわち色を取得することができます。

![precebling.png](https://qiita-image-store.s3.amazonaws.com/0/63241/dff76c84-8de8-7d28-c38e-2a401b6b4b47.png)


## 重複なく抽出

```html

<table>
  <tr>
    <td>レッド</td>
    <td>レッド</td>
    <td>レッド</td>
  </tr>
  <tr>
    <td>ブルー</td>
  </tr>
  <tr>
    <td>グリーン</td>
  </tr>
</table>
```
このHTMLから重複なく色を取得したい場合は下記のように書けます。

```
//td[not(.=preceding::td)]
```
このXPathは、td要素の中でprecedingすなわち前に出てくる要素と一致しないものを取得しています。


# XPath関連便利サービス
最後に、XPathを取得する時にオススメの拡張機能を紹介します。
『XPath Helper』です！！

## XPath Helper
XPath Helperは、ブラウザから要素をカーソルに合わせるだけでXPathを調べることができる超優れたchrome拡張機能です。

<img width="710" alt="helper.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/e7cdb543-6006-1859-3dc9-899b653b2d61.png">



下記からダウンロードができます。
https://chrome.google.com/webstore/detail/XPath-helper/hgimnogjllphhhkhlmebbmlgjoejdpjl?hl=ja

この拡張機能を使い方は、
chromeブックマークバーのxと書いてあるアイコンをクリック、またはショートカットキー[Ctrl + Shift + X]で起動します。
そして、取得したい要素をシフトを押しながら選択すると、要素のXPathが簡単に取得できます。

<img width="1123" alt="tukaikata.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/be51cb81-94d0-0d3e-864d-01b80dbfae39.png">


とても簡単にXPathが取得できるのでオススメです！

# まとめ

以上が、クローラーに便利なXPathまとめでした！
XPathは比較的覚えやすく理解しやすい言語ですので、非エンジニアの方にもとてもオススメです。
ぜひXPath Helperを入れて、XPathを試してみてください！