問題
以下のようなHTMLで「次へ」ボタンをクリックする挙動を再現するために、aタグをクリックさせたいとします。「次へ」という文字列を使いXPathで検索をかけると、どうしても//div/a/span/span[contains(text(), '次へ')]
のようになってしまい、aタグを選択することができません。
だからといって、divタグより上のタグで検索をかけると、//div/a[2]
のようにマジックナンバーを使って何番目の要素か指定したり、//div/a[last()]
のように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の基本知識を学ぼう!」を読んでいて、解決方法を思いつきました。その時の画像です。
ピンクの下線で強調した//li[a]
と//a[contain(text(), 'Next')]
を組み合わせると、//li[a[contain(text(), 'Next')]]
となることに気が付いたのです。
ここまで気が付けば後は簡単です。
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の記法まとめ
- Seleniumで要素を選択する方法まとめ
- W3C All Standards and Drafts XPATH COVER PAGE
- これが定義の原文らしい
- https://www.w3.org/TR/xpath/all/
- XPath の例
#working-with-xpaths
- Windows 10
- Miniconda 3
- Python 3.8.2
- Selenium 3.141.0
- Chrome Driver 80.0.3987.106
PythonとSeleniumのバージョンさえ同じであれば、挙動は同じだと思われます。
Excelsior!