子供を条件にして親を選択するときXpathをどう書く？

# 問題

以下のようなHTMLで「次へ」ボタンをクリックする挙動を再現するために、aタグをクリックさせたいとします。「次へ」という文字列を使いXPathで検索をかけると、どうしても`//div/a/span/span[contains(text(), '次へ')]`のようになってしまい、aタグを選択することができません。

だからといって、divタグより上のタグで検索をかけると、`//div/a[2]`のようにマジックナンバーを使って何番目の要素か指定したり、`//div/a[last()]`のようにHtmlタグのツリー構造に強く依存するなど、苦渋の決断を強いられてしまいます。

```html:
<div class="ui-controlgroup-controls">
    <a href="url1" data-role="button" data-theme="f" data-corners="true" data-shadow="true" data-iconshadow="true" data-wrapperels="span" class="ui-btn ui-btn-up-f ui-shadow ui-btn-corner-all ui-first-child">
        <span class="ui-btn-inner">
            <span class="ui-btn-text">前へ</span>
        </span>
    </a>
    <a href="url2" data-role="button" data-theme="f" data-corners="true" data-shadow="true" data-iconshadow="true" data-wrapperels="span" class="ui-btn ui-btn-up-f ui-shadow ui-btn-corner-all ui-last-child">
        <span class="ui-btn-inner">
            <span class="ui-btn-text">次へ</span>
        </span>
    </a>
</div>
```

# その手があったか

OctoparseのXpath紹介ページ「[XPathとは？XPathの基本知識を学ぼう！](https://www.octoparse.jp/tutorial/xpath/)」を読んでいて、解決方法を思いつきました。その時の画像です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134703/74a2ca00-673e-3ce7-b8b0-6677df4b9e5a.png)
ピンクの下線で強調した`//li[a]`と`//a[contain(text(), 'Next')]`を組み合わせると、`//li[a[contain(text(), 'Next')]]`となることに気が付いたのです。

ここまで気が付けば後は簡単です。

```py:
import selenium
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.keys import Keys
from time import sleep

options = ChromeOptions()
# options.add_argument('--headless')

driver = Chrome(options=options)

#

url = "??????? this is url ?????????"
driver.get(url)
# sleep(0.5)

input_element :selenium.webdriver.remote.webelement.WebElement
# 施設空き状況

input_element = driver.find_element_by_css_selector("施設空き状況ボタンのCSSセレクタ")
input_element.click()
# sleep(0.5)

# sleep(0.5)

input_element = driver.find_element_by_css_selector("地域からボタンのCSSセレクタ")
input_element.click()
# sleep(0.5)

# sleep(0.5)

input_element = driver.find_element_by_xpath("//a[span[span[contains(text(), '次へ')]]]")
input_element.click()
# sleep(0.5)

```

`"//a[span[span[contains(text(), '次へ')]]]"`としましたが、今回のHTMLの場合は`"//a[span/span[contains(text(), '次へ')]]"`でも同様の結果が得られますね。

また、「次へ」という文字列を使って判別していますが、例えば、クラス指定で`//a[span/span[@class='ui-btn-text']]`とか、idがあれば、`//a[span/span[@id='next-button']]`などもできそうです。

Xpathは本当に便利です。Xpathに感謝です。

# 別の方法 (2020/5/14 追記)

この記事で対象にしているHTMLでは、以下のXPATHはすべて同じ結果となります。

- `"//a[span/span[contains(text(), '次へ')]]"`
- `"//a[span[span[contains(text(), '次へ')]]]"`
- `"//a/span/span[contains(text(), '次へ')]/../.."`
- `"//a/span/span[contains(text(), '次へ')]/parent::*/parent::*"`
- `"//a/span/span[contains(text(), '次へ')]/parent::node()/parent::node()"`
- `"//a/span[span[contains(text(), '次へ')]]/.."`
- `"//a/span[span[contains(text(), '次へ')]]/parent::*"`
- `"//a/span[span[contains(text(), '次へ')]]/parent::node()"`

# 参考にしたページ

- クローラ作成に必須！XPATHの記法まとめ
    - https://qiita.com/rllllho/items/cb1187cec0fb17fc650a
- Seleniumで要素を選択する方法まとめ
    - https://qiita.com/VA_nakatsu/items/0095755dc48ad7e86e2f
- W3C All Standards and Drafts XPATH COVER PAGE
    - これが定義の原文らしい
    - https://www.w3.org/TR/xpath/all/
- XPath の例
    - 最近のMicrosoftのドキュメントは見やすいのでオススメ
    - https://docs.microsoft.com/ja-jp/previous-versions/ms256086(v=vs.120)
    - https://doc-ja-scrapy.readthedocs.io/ja/latest/topics/selectors.html#working-with-xpaths

#working-with-xpaths

- Windows 10
- Miniconda 3
- Python 3.8.2
- Selenium 3.141.0
- Chrome Driver 80.0.3987.106

PythonとSeleniumのバージョンさえ同じであれば、挙動は同じだと思われます。

**Excelsior!**