XpathによるXML要素の選択手順

2,325 views
Skip to first unread message

tkxn

unread,
Dec 11, 2018, 3:24:18 PM12/11/18
to 日本Seleniumユーザーコミュニティ
こんにちは、
Python+selenium+chromeで、サイトにログイン後データ収集したいと考えております。
現状、ログインはできたのですが、以下がうまくいきません、どなたかヒントだけでもご教示いただけますと助かります。

問題の部分はやや特殊で、6個の取引メニューごとにプルダウンが配置されており、
プルダウンのラベルをそのままクリックで「新取引システム」、
リストを表示させてクリックで「旧取引システム」画面が開きます。
今回は、FX口座・新取引システムの画面を開こうとしています。
ダッシュボード・プルダウン.jpg
Chrome上でXpathによりXML要素を選択するには、

Chrome画面上で当該箇所をポイントして右クリック、検証を選択してデベロッパーツール上にXMLを反転表示
⇒デベロッパーツール上で、このXMLの部分を右クリック、Copy > CopyXPath でXpathをコピー
⇒スクリプトでXML要素をこのXpathにより選択してクリックする
という手順と理解しています。

例えば、この手順で本Seleniumユーザーコミュニティのテスト用サイトで、
以下の「次へ」をクリックするスクリプトは機能しました(必須項目を入力していないのでエラーになりますが)。
from selenium import webdriver
 
driver
= webdriver.Chrome("c:/driver/chromedriver.exe")
driver
.get("http://example.selenium.jp/reserveApp/")

elem_Next_btn
= driver.find_element_by_xpath("//*[@id='goto_next']")
elem_Next_btn
.click()


ところが、この手順で問題の箇所を以下のとおり操作しようとしましたが、エラーとなりうまくいきません。

1.まず、Chrome画面上でFX口座のプルダウンラベルを右クリックして、検証を選択すると、デベロッパーツールに以下のXML要素が反転表示されました。
<a class="segmented-button-label"
 
ng-class="{ 'center-text': !controller.shouldShowDropdownArrow,
'segmented-button-label-disabled': controller.account.isDisabled }"

ng-click="controller.openFirstPlatform()" data-e2e="openPlatformButton">新取引システム</a>

2.この太字の部分を右クリックして、Copy > CopyXPath を選択するとこの部分のXpathが以下のとおりコピーされました。
//*[@id="my-ig"]/div[2]/div/ig-state-change-
spinner
/div/div/div/div/div/div/div[1]/div/account-
table
/div/div/div[2]/div[3]/div/leveraged-account-table-row/div/div[1]/platform-
segmented
-button/div/a

3.これを利用してログイン処理のあとで当該プルダウンをクリックする以下スクリプトを実行したところ、
elem_segmented_btn = driver.find_element_by_xpath("//*[@id='my-ig']/div[2]/div/ig-
state-change-spinner/div/div/div/div/div/div/div[1]/div/account-
table/div/div/div[2]/div[3]/div/leveraged-account-table-row/div/div[1]/platform-
segmented-button/div/a"
)
elem_segmented_btn
.click()


以下のUnable to locate elementのエラーとなりました。
raise exception_class(message, screen, stacktrace)

selenium
.common.exceptions.NoSuchElementException:
Message: no such element:
Unable to locate element:

XML要素の選択についていろいろと検索していたところ、「Python Selenium でドロップダウンが選択できません」で、
『ドロップダウンメニューのHTMLコードを誤解されています。HTMLソースではなく、開発者ツールなどで確認できるDOMツリーを見ましょう。』
『HTMLソースでは 要素ですが、画面の表示時に 要素に置き換えられています。』など見かけました、
今回のヒントとなるようにも思いますが、開発者ツール(このばあいChromeのデベロッパーツール)どう使えば「書き換え」を確認できるのかよくわかりません。

どなたかヒントだけでもご教示いただけますと助かります、よろしくお願いします。

タカミテツロウ

unread,
Dec 11, 2018, 5:39:38 PM12/11/18
to tkxn...@gmail.com, selen...@googlegroups.com
こんにちは

以下 私が ログイン後のサイトで 嵌った時 参考になったサイトです

2018年12月12日(水) 5:24 tkxn <tkxn...@gmail.com>:
--
このメールは Google グループのグループ「日本Seleniumユーザーコミュニティ」に登録しているユーザーに送られています。
このグループから退会し、グループからのメールの配信を停止するには seleniumjp+...@googlegroups.com にメールを送信してください。
このグループに投稿するには selen...@googlegroups.com にメールを送信してください。
その他のオプションについては https://groups.google.com/d/optout にアクセスしてください。

tkxn

unread,
Dec 11, 2018, 5:57:24 PM12/11/18
to 日本Seleniumユーザーコミュニティ
タカミさま、返信ありがとうございます。いただいた情報、検証しまして報告いたします。

伊藤望

unread,
Dec 12, 2018, 12:07:56 AM12/12/18
to 日本Seleniumユーザーコミュニティ
こんにちは、伊藤です。

> 開発者ツール(このばあいChromeのデベロッパーツール)どう使えば「書き換え」を確認できるのかよくわかりません。

簡易な方法としては、例えばエラーの出る直前の行で長いSleepを入れてテストを止め、
そのブラウザで右クリック検証でXMLツリーを表示し、

//*[@id='my-ig']/div[2]/div/ig-
state-change-spinner/div/div/div/div/div/div/div[1]/div/account-
table/div/div/div[2]/div[3]/div/leveraged-account-table-row/div/div[1]/platform-
segmented-button/div/a

に該当するxpathの要素が存在するか、XMLを目で見て確認、もしくは https://qiita.com/ywindish/items/5a992c49387d81df900e#xpath-%E3%82%92%E6%A4%9C%E8%A8%BC%E3%81%99%E3%82%8B の方法で確認できます。

※ Seleniumテスト実行中にデベロッパーツールが起動できなかったらごめんなさい。

ただ、このxpathは長すぎて解読困難ですし、
テストを作った時と実行した時でちょっと画面の状態が違うだけで影響を受けて、要素が見つからなくなってしまうと思います。

Selenium IDEで要素を記録すれば、もっと画面変化に強いXpathやcssセレクタとれると思うので、
そちらで記録してWebDriverコードに流用してくるのがいいと思います。

On Wednesday, December 12, 2018 at 7:57:24 AM UTC+9, tkxn wrote:
タカミさま、返信ありがとうございます。いただいた情報、検証しまして報告いたします。

tkxn

unread,
Dec 12, 2018, 4:04:45 PM12/12/18
to 日本Seleniumユーザーコミュニティ
伊藤さま、返信いただきありがとうございました。
いただいた情報を検証しまして報告いたします。
SeleniumIDEなど初めて接する部分もありますので、若干時間を要すると思いますが、

2018年12月12日水曜日 14時07分56秒 UTC+9 伊藤望:

tkxn

unread,
Dec 18, 2018, 6:54:49 AM12/18/18
to 日本Seleniumユーザーコミュニティ
伊藤さま、皆さま、質問者のtkxnです。

先日の件の経過報告です。
SleniumIDEでは実行できたのですが、pythonに移す段階でエラーとなります。

先ず、
>SeleniumIDEで要素を記録すれば、もっと画面変化に強いXpathやcssセレクタとれると思うので、
そちらで記録してWebDriverコードに流用してくるのがいいと思います。

をヒントに、seleniumIDEの最新版をインストール、SeleniumIDEで今回のサイトへのログインから
問題のプルダウンをクリックして次の画面を開く箇所までをTESTに記録、TEST実行で再現可能を確認しました。

次に、IDEの以下の箇所よりプルダウンのCSSセレクターおよびxpathを取得

seleniumIDE・Target.jpg

なお、
>このxpathは長すぎて解読困難ですし、テストを作った時と実行した時でちょっと画面の状態が違うだけで影響を受けて、要素が見つからなくなってしまうと思います。

とのことでしたので、3個目以降は使用しませんでした。

以上から、クリックする部分のスクリプト
CSSセレクターの場合、
elem_segmented_btn = driver.find_element_by_css_selector(".account:nth-child(3) .segmented-button-label")
elem_segmented_btn
.click()

xpathの場合、
elem_segmented_btn = driver.find_element_by_xpath("(//a[contains(text(),'新取引システム')])[3]")
elem_segmented_btn
.click()


で実行してみたのですが、いずれも
raise exception_class(message, screen, stacktrace)
selenium
.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element

のエラーになります。

単純なsyntaxエラーではないようですし、IDEの最新版にはpythonスクリプトにエクスポートする機能は実装されてないようですし、
何か解決の方法はありませんでしょうか?


2018年12月12日水曜日 14時07分56秒 UTC+9 伊藤望:

Nozomi Ito

unread,
Dec 19, 2018, 9:14:37 PM12/19/18
to tkxn, 日本Seleniumユーザーコミュニティ
伊藤です。

> とのことでしたので、3個目以降は使用しませんでした。
それが良いと思います!

> 簡易な方法としては、例えばエラーの出る直前の行で長いSleepを入れてテストを止め、
> そのブラウザで右クリック検証でXMLツリーを表示し、

こちらの方法で、エラーの瞬間に、例えば下記要素が存在するか調べてみてください。
別ページにいる、画面構成がテスト作成時と違う、などの理由で存在しない可能性があります。

"(//a[contains(text(),'新取引システム')])[3]"
(新取引システムというテキストを含むa要素のうち上から3番目のもの)

もしくは、Selenium IDEで動くのでしたら、タイミングの問題かもしれません。
implicitly_wait ( https://kurozumi.github.io/selenium-python/waits.html )などを使えばタイミングが早すぎて要素が出てくる前に失敗する、といった問題を回避できる可能性があります。

ちなみにCSSの方は
".account:nth-child(3) .segmented-button-label"
ですが、.account:nth-child(3) というのは、
accountクラス属性を持つ兄弟要素のうち3番目、という意味だと思うんですが、
親が指定されていないのでいったい誰を親とする兄弟なんですかね。。ルート要素?


--

tkxn

unread,
Dec 20, 2018, 3:29:30 PM12/20/18
to 日本Seleniumユーザーコミュニティ
伊藤さま、返信ありがとうございました!

とりあえず
>もしくは、Selenium IDEで動くのでしたら、タイミングの問題かもしれません。
mplicitly_wait ( https://kurozumi.github.io/selenium-python/waits.html )などを使えばタイミングが早すぎて要素が出てくる前に失敗する、といった問題を回避できる可能性があります。

あたりからまた検証してみますので、少しお時間いただければと思います、といっても結構時間がかかってしまうのですが(笑)

ところで、
>ちなみにCSSの方は
".account:nth-child(3) .segmented-button-label"
ですが、.account:nth-child(3) というのは、
accountクラス属性を持つ兄弟要素のうち3番目、という意味だと思うんですが、
親が指定されていないのでいったい誰を親とする兄弟なんですかね。。ルート要素?

ということは、seleniumIDEに表示されたxpathやcssでも、pythonなどに移すとき加工が必要な場合があるということでしょうか?


2018年12月20日木曜日 11時14分37秒 UTC+9 伊藤望:

Nozomi Ito

unread,
Dec 20, 2018, 7:42:26 PM12/20/18
to tkxn, 日本Seleniumユーザーコミュニティ
> ということは、seleniumIDEに表示されたxpathやcssでも、pythonなどに移すとき加工が必要な場合があるということでしょうか?

いえ。私が ".account:nth-child(3) .segmented-button-label"の意味を理解していないだけです。

わかる方いたら教えてください。

タカミテツロウ

unread,
Dec 20, 2018, 8:43:57 PM12/20/18
to tkxn, 日本Seleniumユーザーコミュニティ
エラーの内容は elementが 見つからないということなので
ログイン後に 以下のコードを実行して
存在するかどうか 確認されてみては 如何でしょうか?

print (driver.find_element_by_tag_name('body').get_attribute('innerHTML'))

***************************************************************************************************
また、element の表示待ちまで 待つのなら 下記コードでも 良いかもです
(elementが 表示されないと ループしてしまいますが)


from time import sleep

elem_segmented_btn = driver.find_element_by_xpath("//*[@id='my-ig']/div[2]/div/ig-
state-change-spinner/div/div/div/div/div/div/div[1]/div/account-
table/div/div/div[2]/div[3]/div/leveraged-account-table-row/div/div[1]/platform-
segmented-button/div/a")
while elem_segmented_btn.is_displayed() == False:
sleep(0.01)
elem_segmented_btn.click()  
 


2018年12月21日(金) 5:29 tkxn <tkxn...@gmail.com>:

tkxn

unread,
Dec 20, 2018, 10:32:16 PM12/20/18
to 日本Seleniumユーザーコミュニティ
タカミさま、返信ありがとうございます!
コード試してみてご報告いたします。
tkxn

戸田広

unread,
Dec 22, 2018, 8:21:02 AM12/22/18
to tkxn, 日本Seleniumユーザーコミュニティ
なお
    .account:nth-child(3) .segmented-button-label
というCSSセレクターは、
DOMツリーの評価は親要素からではなく子要素の末端からになるのと、
nth-child(n) は兄弟要素という意味だけで、特定の親要素を決めないことから、
例えば次のようなHTMLがあると2か所にヒットします。

<html>
<head>
<style>
.account:nth-child(3) .segmented-button-label {
    background-color: red;
}
</style>
</head>
<body>
<div class="root">
    <div class="account">
        <span class="segmented-button-label">hoge1</span>
    </div>
    <div class="account">
        <span class="segmented-button-label">hoge2</span>
    </div>
    <div class="account">
        <span class="segmented-button-label">hoge3</span>
    </div>
    <div class="account">
        <span class="segmented-button-label">hoge4</span>
    </div>
    <div class="sub">
        <div class="account">
            <span class="segmented-button-label">hoge5</span>
        </div>
        <div class="account">
            <span class="segmented-button-label">hoge6</span>
        </div>
        <div class="account">
            <span class="segmented-button-label">hoge7</span>
        </div>
        <div class="account">
            <span class="segmented-button-label">hoge8</span>
        </div>
    </div>
</div>
</body>
</html>

次のようになります。
スクリーンショット 2018-12-22 22.19.11.png



2018年12月21日(金) 12:32 tkxn <tkxn...@gmail.com>:
タカミさま、返信ありがとうございます!
コード試してみてご報告いたします。
tkxn

--
このメールは Google グループのグループ「日本Seleniumユーザーコミュニティ」の登録者に送られています。

Nozomi Ito

unread,
Dec 22, 2018, 9:29:32 PM12/22/18
to 日本Seleniumユーザーコミュニティ
なるほど!戸田さんありがとうございます!

タカミテツロウ

unread,
Dec 22, 2018, 10:07:12 PM12/22/18
to tkxn, 日本Seleniumユーザーコミュニティ
こんにちは

長い Xpath については 以下のサイトが 参考になるかもです
私は Xpathの記述を 短くするときに 使用しております


**********************************************************************************
また、私が Aタグをクリックする時に たまにする邪道な手法で

例えば
<a href="http://www.htmq.com/html/a.shtml">絶対パスでリンクします</a><br> 
であれば
リンク先に 直接ページ遷移します





2018年12月21日(金) 12:32 tkxn <tkxn...@gmail.com>:
タカミさま、返信ありがとうございます!
コード試してみてご報告いたします。
tkxn

--
このメールは Google グループのグループ「日本Seleniumユーザーコミュニティ」の登録者に送られています。

タカミテツロウ

unread,
Dec 22, 2018, 10:53:46 PM12/22/18
to tkxn, 日本Seleniumユーザーコミュニティ
href属性が 無いようでしたね
大変失礼致しました

検証ができませんが 
print (driver.find_element_by_tag_name('body').get_attribute('innerHTML')) で要素が 存在すれば
   (存在しない場合 別の原因が あると思われます)
  以下が 省略形の一つの例です

elem_segmented_btn = driver.find_element_by_xpath("//a[contains(text(), '新取引システム')]")
elem_segmented_btn.click()


2018年12月21日(金) 12:32 tkxn <tkxn...@gmail.com>:
タカミさま、返信ありがとうございます!
コード試してみてご報告いたします。
tkxn

--
このメールは Google グループのグループ「日本Seleniumユーザーコミュニティ」の登録者に送られています。

タカミテツロウ

unread,
Dec 23, 2018, 4:36:36 PM12/23/18
to tkxn, 日本Seleniumユーザーコミュニティ
連投で 失礼致します

新取引システムが 複数あり、「FX口座・新取引システム」は 上から 3番目みたいなので
elem_segmented_btn = driver.find_element_by_xpath("//a[contains(text(), '新取引システム')][3]")
elem_segmented_btn.click()
でしょう

************************************************************************************************
************************************************************************************************
はじめからの疑問なのですが
該当要素が frameか ifarme内に  存在する可能性は ないでしょうか?
(この場合も 同様のエラーが 発生します)

以下のコードで 【発見しました!】と 表示されるとよいのですが…

参考コード

from selenium import webdriver 

from time  import sleep


chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--disable-infobars")
driver = webdriver.Chrome(chrome_options=chrome_options)

driver.get('http://www.htmq.com/html5/iframe.shtml') #この行を コメントアウトする


# *******************************************

#   ここに ログイン操作を 記述する

# *******************************************


def frame_iframe(ls):
    for  l in ls:
        iframe_HTML = (l.get_attribute('outerHTML'))
        print (iframe_HTML +'\n' )

        try:
            driver.switch_to_frame(l)
            sleep (0.1)
            Str = driver.page_source
            
            if Str.find('新取引システム') != -1:
                
                print ('*' * 30 )
                print ('発見しました!')
                print ('*' * 30 )
                
                try:
                    driver.find_element_by_xpath("//a[contains(text(), '新取引システム')][3]").click()
                    break
                except:
                    pass
                          
        except:
            pass 

        driver.switch_to_default_content() 
        sleep (0.1) 


ls = driver.find_elements_by_tag_name('frame')

if len(ls) > 0:
    print ('*' * 30 )
    print('','frameタグの数:' + str(len(ls)) + '個です')
    print ('*' * 30 +'\n')

    frame_iframe(ls)
    


ls = driver.find_elements_by_tag_name('iframe')

if len(ls) > 0:
    print ('*' * 30 )
    print('','iframeタグの数:' + str(len(ls)) + '個です')
    print ('*' * 30 +'\n')

    frame_iframe(ls)

    

sleep (3)

driver.quit()

tkxn

unread,
Dec 23, 2018, 4:38:20 PM12/23/18
to 日本Seleniumユーザーコミュニティ
皆さま、質問しましたtkxnです。
初学者のわたくしの問い合わせにいろいろとご返信いただき恐縮です。

そのなかで、タカミさまの、
>エラーの内容は elementが 見つからないということなのでログイン後に 以下のコードを実行して存在するかどうか 確認されてみては 如何でしょうか?
print (driver.find_element_by_tag_name('body').get_attribute('innerHTML'))

を実行してみたところ、どうも今回のプルダウンを含むページに切り替わっていないようです。
大量のHTMLが出力され確信はないのですが、比較としてテスト用サイトに対し以下のスクリプトを実行したところ、「対象画面の右クリック>ページのソースを表示」で取得されるHTMLと全く同じHTMLが出力されましたが、今回は内容が相違しているようです。

from selenium import webdriver


driver
= webdriver.Chrome("c:/driver/chromedriver.exe")
driver
.get("http://example.selenium.jp/reserveApp/")


print(driver.find_element_by_tag_name('body').get_attribute('innerHTML'))


これはどういう意味なのでしょうか?

なお、現状、いろいろとアドヴァイスいただいても、私が検証のうえ状況を的確に説明できないため、返信いただいたかたに今回のpythonスクリプトなど全体をお送りいたしました。解決策が見つかりましたら皆さまに共有させていただきたいと思います。


2018年12月12日水曜日 5時24分18秒 UTC+9 tkxn:

tkxn

unread,
Dec 23, 2018, 6:26:05 PM12/23/18
to 日本Seleniumユーザーコミュニティ
タカミさま、ありがとうございます。

>はじめからの疑問なのですが該当要素が frameか ifarme内に 存在する可能性は ないでしょうか?(この場合も 同様のエラーが 発生します)

ifarmeが存在しているようです。教えていただいたHTML出力結果にifarmeのタグがあったようです。
先ほど、pythonのスクリプト全体など送ったのですが例示いただいたスクリプトは自分で検証してみます!

タカミテツロウ

unread,
Dec 23, 2018, 8:43:14 PM12/23/18
to tkxn, 日本Seleniumユーザーコミュニティ
iframe内には なかったので
ログイン操作後に

sleep (15)

elem_Next_btn = driver.find_elements_by_xpath("//a[contains(text(), '取引システム')]")[2]
elem_Next_btn.click()

sleep (60)

で 大丈夫そうです



2018年12月24日(月) 8:26 tkxn <tkxn...@gmail.com>:
--
このメールは Google グループのグループ「日本Seleniumユーザーコミュニティ」の登録者に送られています。

tkxn

unread,
Dec 28, 2018, 2:23:41 AM12/28/18
to 日本Seleniumユーザーコミュニティ
タカミさま、みなさま、質問者tkxnです。

タカミさまにご教示いただいたとおり、
sleep(15)

elem_segmented_btn
= driver.find_element_by_xpath("(//a[contains(text(),'新取引システム')])[3]")
elem_segmented_btn
.click()

sleep
(60)
としたところ、あっさり次のページが開きました。感謝、感謝です!

なお、伊藤さまの以下の部分を参考に、
>もしくは、Selenium IDEで動くのでしたら、タイミングの問題かもしれません。
>implicitly_wait ( https://kurozumi.github.io/selenium-python/waits.html )などを使えばタイミングが早すぎて要素が出てくる前に失敗する、
>といった問題を回避できる可能性があります。

スクリプトの冒頭に、
from selenium import webdriver
driver
= webdriver.Chrome("c:/driver/chromedriver.exe")

driver
.implicitly_wait(60)
をいれることでも正常に次のページを開くことができました。ただ、implicitly_waitの値を10と短くするとエラーとなりました。

とのことですので、今後は、常にdriver.implicitly_waitを待機時間を長めにとってスクリプトの冒頭に入れることといたします。

みなさま、どうもありがとうございました、今後ともよろしくお願いいたします。





2018年12月12日水曜日 5時24分18秒 UTC+9 tkxn:
こんにちは、
Python+selenium+chromeで、サイトにログイン後データ収集したいと考えております。
現状、ログインはできたのですが、以下がうまくいきません、どなたかヒントだけでもご教示いただけますと助かります。

問題の部分はやや特殊で、6個の取引メニューごとにプルダウンが配置されており、
プルダウンのラベルをそのままクリックで「新取引システム」、
リストを表示させてクリックで「旧取引システム」画面が開きます。
今回は、FX口座・新取引システムの画面を開こうとしています。
ダッシュボード・プルダウン.jpg
Chrome上でXpathによりXML要素を選択するには、

Chrome画面上で当該箇所をポイントして294/右クリック、検証を選択してデベロッパーツール上にXMLを反転表示
Reply all
Reply to author
Forward
0 new messages