idが重複する場合
面白いものを見つけましたので紹介します。
ParcelForce
イギリスの郵便会社(Royal Mail)のグループ企業で、小包の配送をしています。日本からイギリスに郵便小包を送ると、この会社が配送を担当するそうです。
さて、こちらの画面では、見積もりができるようです。

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

XPathの取得
XPathGetter
XPathGetterで画面項目のXPathを取得してみます。まず荷物情報が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を取得してみます。

項目名(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を取得してみます。

荷物欄 | 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で動きを見てみます。

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

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

2段目を操作するには、//div[3][@class="ng-scope"]//input[@id="weight"] と入力する必要があります。
まとめ
HTMLの規定では、同一のページではid属性は重複してはならないことになっています。しかしながら、idが重複していても、そのHTMLファイルはエラーとならず正常に表示できるため、今回のケースのように同じidが指定されている可能性を排除できません。
ChromeでXPathを取得した場合は重複チェックはしてくれませんので、スクリプトを実行するまでバグを発見できない可能性があります。
一方、XpathGetterは、idが重複していても、要素を特定できるXPathを編集します。またCodeChekerを用いれば重複チェックを実施しますので、エラーの発見に役立ちます。
ディスカッション
コメント一覧
まだ、コメントがありません