idが重複する場合

面白いものを見つけましたので紹介します。

ParcelForce
イギリスの郵便会社(Royal Mail)のグループ企業で、小包の配送をしています。日本からイギリスに郵便小包を送ると、この会社が配送を担当するそうです。

さて、こちらの画面では、見積もりができるようです。

見積画面
荷物情報1段

「Add Another Size」をクリックすると、追加で荷物情報を入力できます。

見積画面2段
荷物情報2段

XPathの取得

XPathGetter

XPathGetterで画面項目のXPathを取得してみます。まず荷物情報が1段の場合から、

Xpath取得(荷物情報1段)
荷物情報1段
項目名 XPath
From Country //select[@id="collection-country"]
Postcode //input[@id="collectionpostcode"]
To Country //select[@id="delivery-country"]
Postcode //input[@id="deliverypostcode"]
Qty //select[@id="quantity-0″]
Weight //input[@id="weight"]
Length //input[@id="length-0″]
Width //input[@id="width-0″]
Height //input[@id="height-0″]
Add Another Size //span[contains(text(),'Add Another Size')]
Quote & Book //button[@class="btn btn-quote pull-right"]

Weightだけ、序数(0)がないことが気になります。次は2段目のXPathを取得してみます。

Xpath取得(荷物情報2段)
項目名(2段目) XPath
Qty //select[@id="quantity-1″]
Weight //div[3][@class="ng-scope"]//input[@id="weight"]
Length //input[@id="length-1″]
Width //input[@id="width-1″]
Height //input[@id="height-1″]

Weightだけが、長く編集されています。他の項目はid属性に序数(1)が指定されているので、idだけでHTML要素を特定できますが、Weightは @id="weight" でidが重複しているのが原因です。

もともと1段目のXPathは「//input[@id="weight"]」でしたが、2段目が増えると要素を特定できなくなりました。では、一段目のXPathはどうなるのでしょうか?
再度取得したところ、//div[2][@class="ng-scope"]//input[@id="weight"] となりました。

まとめると、次の通りです。

荷物欄 Weight XPath
荷物情報が1段 1段目 //input[@id="weight"]
荷物情報が2段 1段目 //div[2][@class="ng-scope"]//input[@id="weight"]
2段目 //div[3][@class="ng-scope"]//input[@id="weight"]

荷物情報欄の追加は、もともと要素が存在している項目をスタイルシートを用いて表示/非表示のコントロールをしているのではなく、DOMを動的に編集しているようです。

Chrome

では、Chromeの場合はどうでしょうか? ChromeでXPathを取得してみます。

Xpath取得(Chrome)
荷物欄 Weight XPath full XPath
1段 1段目 //*[@id="weight"] /html/body/div[2]/div/div[4]/div[2]/div[1]/div/
form/div[2]/div/div/div[5]/div/input
2段 1段目 //*[@id="weight"] /html/body/div[2]/div/div[4]/div[2]/div[1]/div/
form/div[2]/div/div/div[5]/div/input
2段目 //*[@id="weight"] /html/body/div[2]/div/div[4]/div[2]/div[1]/div/
form/div[3]/div/div/div[5]/div/input

Chromeは、idが重複していると正しいXPathを取得できないようです。full XPathを使用するか、自分でXPathを編集する必要があります。

CodeCheckerで検証

Seleniumの仕様としては、XPathが重複している場合、最初に見つかった要素(1番目の要素)を返します。CodeChekerで動きを見てみます。

CodeChecker(重複要素の取得)
重複要素の取得

要素が2個取得される旨のメッセージが表示されます。参考情報として、生成コード欄に1番目の要素を取得するPythonコードが出力されます。

キー入力してみます。(Sene_keyをクリック)

CodeChecker(重複要素の操作)
重複要素の操作

やはり、1段目が入力されます。

CodeChecker(2段目の操作)
2段目の操作

2段目を操作するには、//div[3][@class="ng-scope"]//input[@id="weight"] と入力する必要があります。

まとめ

HTMLの規定では、同一のページではid属性は重複してはならないことになっています。しかしながら、idが重複していても、そのHTMLファイルはエラーとならず正常に表示できるため、今回のケースのように同じidが指定されている可能性を排除できません。

ChromeでXPathを取得した場合は重複チェックはしてくれませんので、スクリプトを実行するまでバグを発見できない可能性があります。
一方、XpathGetterは、idが重複していても、要素を特定できるXPathを編集します。またCodeChekerを用いれば重複チェックを実施しますので、エラーの発見に役立ちます。

2020-11-21XpathGetterid,Xpath,重複

Posted by sj