初めに
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の記法を学びましょう(自戒)
参考