初めに
Seleniumを使っていると、Webサイトによっては同名のクラス名やidなどが存在して思うようにデータが取れない場合があります。
FullXPATHで指定することもできますが、少しでもWebの要素が変わると全てのXPATHを書き直す必要が出てくるかもしれません。
ある要素を軸に階層移動出来れば楽になりそうだと思ったので、色々と調べてまとめました。
バージョン
- selenium: v4.7.2
要素の取得
単一要素であればfind_element
、複数の要素であればfind_elements
で取得できます。
取得した要素は、WebElement
型になります(複数の場合はlist[WebElement]
)。
# 単一要素
elm = driber.find_element(By.CLASS_NAME, "class_name")
# 複数要素
plants = driver.find_elements(By.TAG_NAME, "li")
elm = plants[0]
このelm
から階層移動することが出来ます。
親要素の取得
現在のWebElement
がelm
という名前で、そこから1階層だけ上に行きたい場合は、下記のように書きます
elm.find_element(By.XPATH, "..")
2階層上に行きたい場合は、
elm.find_element(By.XPATH, "../..")
と書くだけです。
因みにWebElement
はparent
を持っていますが、これは親階層ではなく、その要素が属するWebDriver
インスタンスへの内部参照を返します(参照)
子要素の取得
XPATH以外
これはドキュメントを読めば分かります。軽く触れておきましょう。
HTMLが下記の構成だと仮定します。
<ol id="vegetables">
<li class="potatoes">…
<li class="onions">…
<li class="tomatoes"><span>Tomato is a Vegetable</span>…
</ol>
<ul id="fruits">
<li class="bananas">…
<li class="apples">…
<li class="tomatoes"><span>Tomato is a Fruit</span>…
</ul>
そして、<ul id="fruits">
の子要素である<li class="tomatoes">
を取得したい場合は下記のように書きます。
fruits = driver.find_element(By.ID, "fruits")
tomatoes = fruits.find_element(By.CLASS_NAME,"tomatoes")
WebElement
属性のfind_element
は、その属性の子要素を探索してくれます。
→ 上記のように検索しても、子要素にはないvegetables
のtomatoes
は取得されない。
XPATH
XPATHも基本的には同じですが、XPATHの中身が少し異なります。
今回はHTMLが下記の構成だと仮定します。
<ul id="foods">
<li class="fruits" title="bananas">…
<li class="fruits" title="apples">…
<li class="fruits" title="tomatoes"><span>Tomato is a Fruit</span>…
</ul>
<ul id="fruits">
<li class="fruits" title="bananas">…
<li class="fruits" title="apples">…
<li class="fruits" title="tomatoes"><span>Tomato is a Fruit</span>…
</ul>
<ul id="fruits">
の方の<li class="fruits" title="bananas">
を取得してみましょう。
まずはfruits
の要素にアクセスして、そこからtitle=bananas
を指定して取得する方法を考えてみます。
seleniumでは、By.TITLE
がないのでXPATHで書く必要が出てきます。
→ //li[@title="bananas"]
これを組み合わせると下記のように書けます。
fruits = driver.find_element(By.ID, "fruits")
bananas = fruits.find_element(By.XPATH, "//li[@title='bananas']")
しかし、このように書いても両方(foods, fruits)のbananasが取得されてしまいます。
これはXPATHの//
に原因があり、この//
はルートから検索されてしまいます。
WebElement
で現在いる階層を指定しても、XPATH
を書き間違えると思うような動作が出来ないので注意が必要です。
子の要素をXPATHで取得する際は、XPATHの前に.
を書いてあげましょう。
bananas = fruits.find_element(By.XPATH, ".//li[@title='bananas']")
これでfruits
のtomatoes
要素だけが取得出来ました。
終わりに
XPATH
を扱うのであれば、XPATH
の記法を学びましょう(自戒)
参考