【Excel VBA×Selenium】マウスオーバーで表示されるメニューを操作する方法

みなさんこんにちは。

Excel VBA×SeleniumでWebサイトを操作していると、クリックしたいメニューが最初から表示されておらず、親メニューへマウスを乗せたときだけサブメニューが表示されるページに出会うことも多いかと思います。

このようなメニューでは、通常の要素取得とClickだけでは目的の項目を操作できない場合があります。

SeleniumBasicでは、Actions.MoveToElementを使用することで、実際に対象要素へマウスを移動したような操作を実行できます。

今回は、Excel VBA×Seleniumで親メニューへマウスオーバーし、表示されたサブメニューを取得してクリックする方法についてご紹介したいと思います。

この記事はこんな人におすすめ

  • マウスを乗せたときだけ表示されるメニューをSeleniumで操作したい人
  • VBA×SeleniumでMouseOverやhoverの処理を実装したい人
  • 通常のClickだけではサブメニューを操作できず困っている人
  • Actions.MoveToElementの使い方を知りたい人
  • マウスオーバー後に表示待機してからメニューをクリックしたい人

今回操作するページのイメージ

今回は、Webページ上に「商品」という親メニューがあり、そこへマウスを乗せると「商品一覧」というサブメニューが表示されるケースを例にします。

HTMLのイメージは次のようになります。

<div id="product-menu" class="menu-item">
    商品

    <ul class="submenu">
        <li>
            <a id="product-list" href="#products">
                商品一覧
            </a>
        </li>
    </ul>
</div>

サブメニューはCSSやJavaScriptによって最初は非表示となっており、親メニューへマウスを乗せたときに表示されるものとします。

Seleniumでマウスオーバーする前のサブメニューが非表示になっている画面

親メニューの要素を取得する

まずは、マウスを移動させる対象となる親メニューを取得します。

Dim menuElement As Selenium.WebElement

'========================================
' マウスオーバーする親メニューを取得する
'========================================
Set menuElement = driver.FindElementById("product-menu")

ここではidを使用していますが、実際のWebサイトではXPathやclassなど、取得しやすい方法へ置き換えてください。

Actions.MoveToElementでマウスオーバーする

取得した要素へマウスを移動するには、SeleniumBasicのActionsを使用します。

'========================================
' 親メニューの中央へマウスを移動する
' Performを実行することで実際の操作が行われる
'========================================
driver.Actions.MoveToElement(menuElement).Perform

MoveToElementだけではなく、最後にPerformまで実行することがポイントとなります。

Seleniumの公式ドキュメントでも、MoveToElementは対象要素の表示領域内の中央へマウスを移動する、いわゆるhover操作として説明されています。

Selenium公式:Mouse actions

マウス移動が成功すると、ページ側のCSSやJavaScriptによってサブメニューが表示されます。

Seleniumの操作対象となる商品メニューへマウスオーバーして商品一覧が表示された画面

サブメニューが表示されるまで少し待つ

Webサイトによっては、マウスを乗せた直後ではなく、アニメーションなどによって少し遅れてサブメニューが表示される場合があります。

そのような場合は、マウスオーバー後に短い待機時間を入れてから次の要素を取得すると安定しやすくなります。

'========================================
' サブメニューが表示されるまで少し待機する
' 単位はミリ秒
'========================================
driver.Wait 500

今回は例として500ミリ秒待機しています。固定時間を長くしすぎると処理全体が遅くなるため、実際のページの表示速度を確認しながら調整してください。

表示されたサブメニューを取得してクリックする

サブメニューが表示されたら、通常の要素取得と同じように対象要素を取得してクリックします。

Dim subMenuElement As Selenium.WebElement

'========================================
' マウスオーバー後に表示された
' サブメニューの要素を取得する
'========================================
Set subMenuElement = driver.FindElementById("product-list")

'========================================
' 取得したサブメニューをクリックする
'========================================
subMenuElement.Click

ここまでで、親メニューへマウスを移動し、表示されたサブメニューをクリックする一連の操作が完成します。

マウスオーバーからクリックまでの実務コード

ここまでの処理をまとめると、次のようになります。

Sub MouseOverMenuSample()

    Dim driver As New Selenium.WebDriver
    Dim menuElement As Selenium.WebElement
    Dim subMenuElement As Selenium.WebElement

    '========================================
    ' Chromeを起動する
    '========================================
    driver.Start "chrome"

    '========================================
    ' 操作対象となるWebページを表示する
    ' 実際のURLへ変更してください
    '========================================
    driver.Get "https://example.com/"

    '========================================
    ' マウスオーバーする親メニューを取得する
    '========================================
    Set menuElement = _
        driver.FindElementById("product-menu")

    '========================================
    ' 親メニューへマウスを移動する
    ' Performまで実行することで操作が確定する
    '========================================
    driver.Actions.MoveToElement(menuElement).Perform

    '========================================
    ' サブメニューが表示されるまで少し待機する
    '========================================
    driver.Wait 500

    '========================================
    ' 表示されたサブメニューを取得する
    '========================================
    Set subMenuElement = _
        driver.FindElementById("product-list")

    '========================================
    ' サブメニューをクリックする
    '========================================
    subMenuElement.Click

    '========================================
    ' 動作確認のため少し待機する
    '========================================
    driver.Wait 1000

    '========================================
    ' Chromeを終了する
    '========================================
    driver.Quit

End Sub

実際のWebサイトでは、親メニューやサブメニューのidが存在しない場合もあります。その場合はXPathやCSSセレクターなどを使って対象要素を取得してください。

マウスオーバーがうまく動かない場合

対象要素が画面内に表示されているか確認する

MoveToElementは対象となる要素へマウスを移動する処理です。対象要素が画面外にある場合や、別の要素に覆われている場合は正常に操作できないことがあります。まずは対象となる親メニューがブラウザ上に表示されているか確認してください。

マウスオーバー後すぐに取得しない

サブメニューがアニメーション付きで表示される場合、MoveToElementの直後に取得・クリックすると、まだ表示が完了していないことがあります。

その場合は今回のように短い待機を入れ、メニューが表示されてから取得してください。

JavaScriptで制御されているメニューに注意する

Webサイトによっては、単純なCSSの:hoverではなく、JavaScriptのmouseoverやmouseenterなどのイベントでメニューを制御している場合があります。

Actions.MoveToElementで正常に表示されるページも多いですが、サイト独自のイベント処理によっては期待どおりに動かない場合があります。

その場合は、まずChromeのデベロッパーツールで、親メニューへ実際にマウスを乗せたときにどの要素が表示されるのかを確認することが大切です。

JavaScriptから直接クリックする方法もありますが、実際のマウス操作で発生するイベントを省略してしまうことがあります。そのため、最初からJavaScriptクリックへ置き換えるのではなく、まずは通常のMoveToElementとClickで操作できないか確認するのがおすすめです。

XPathを使ってメニューを取得する場合

idやclassが動的に変わるページでは、XPathのcontains()を使って要素を取得する方法もあります。

Set menuElement = driver.FindElementByXPath( _
    "//div[contains(@class, 'menu-item')]" _
)

XPathのcontains()による部分一致については、以下の記事で詳しくご紹介しています。

【Excel VBA×Selenium】XPathのcontainsで部分一致する要素を取得・クリックする方法

最後に

いかがでしたでしょうか。

今回は、Excel VBA×Seleniumでマウスオーバーすると表示されるメニューを操作する方法についてご紹介しました。

親メニューを通常のクリックで操作できない場合でも、Actions.MoveToElementを使ってマウスを移動することで、サブメニューを表示できる場合があります。

また、マウスオーバー後にサブメニューが表示されるまで少し時間がかかる場合は、短い待機を入れてから要素を取得すると安定しやすくなります。

Webサイトによってメニューの実装方法は異なるため、うまく動かない場合はHTML構造やJavaScriptイベントも確認してみてください。

マウスを乗せないと表示されないメニューを自動操作するときに、少しでも参考になれば幸いです。

開発依頼について

ココナラでWebスクレイピング開発サービスを出品しております。

自分で開発をしようと思ったけど、VBAでのネット記事が少なく困っている方も多いかと思います。

そんな方はいつでもお気軽にご相談ください。

また、本ブログからご依頼いただいた方については割引特典がございますので、

ご不明点と合わせてメッセージをいただけると幸いです。

Excelにてブラウザ操作自動化ツールを作成します その作業、webスクレイピングを使って自動化しましょう!

Webスクレイピング以外にもWebアプリの開発サービスも出品しております。

こちらについても、本ブログからご依頼いただいた方については割引特典がございますので、

お気軽にご相談ください。

スモールスケールのWebアプリ開発します 先ずはスモールスケールのWebアプリから始めませんか?

ご興味がある方はこちら!!

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA