【问题标题】:Selenium: Unable to Extract Child Element from ParentSelenium:无法从父元素中提取子元素
【发布时间】:2019-07-21 11:51:46
【问题描述】:

我正在尝试使用结构点击元素:

parentElement.findElement(XPath of Child Element);

但是,这会抛出 org.openqa.selenium.NoSuchElementException

我目前正在使用XPath.//*[@id='savedCartViewForm']/div/div 抓取(父)元素列表。这标识了正确的元素列表(按预期工作)。获得列表后,我将应用该功能:

parentElement.findElement(By.xpath(XPath));

使用XPath

.//input[contains(@aria-label,'Delete')]

.//input[contains(@value,'Delete')]

但是这会触发org.openqa.selenium.NoSuchElementException

我在 SO 上对此进行了几个小时的研究,并根据几篇帖子添加了“。”在两个斜杠之前,表示相对于父级(而不是相对于整个 DOM)。

如果我删除点,我总是会得到页面上的第一个元素 - 而不是列表中每个父元素的子元素。

HTML 的结构如下:

<form id="savedCartViewForm" action="/gp/cart/view.html/ref=ord_cart_shr?app-nav-type=none&dc=df" method="post">
  <input type="hidden" value="1" name="fromAUI" />
  <input type="hidden" value="4CPM1MKXXXXXZ" name="requestID" />
  <input type="hidden" value="15XXXXXX0" name="timeStamp" />
  <input type="hidden" value="gkAVhUvXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAFx2yhxAAA" name="token" />
  <div class="a-divider a-divider-section">
    <div class="a-row sc-list-head sc-java-remote-feature">
      <div class="a-row sc-list-body sc-java-remote-feature">
        <div class="a-row sc-list-item sc-list-item-border sc-java-remote-feature" data-quantity="4" data-price="8.99" data-previous-offer-id="ekOCOsC%2Bl7B8l5MNqXXXXXXXXXXXXXXXXXXXXXXXXXXbBFS5rQm%2BDi9cGGpFufHEITXWAr6tAjIiPTFbZiXjZyIce7Y" data-outofstock="0"
          data-minquantity="1" data-itemtype="saved" data-itemislastpantryitem="0" data-itemid="S2e1cb5b5-ebb5-4e70-8474-c596d80bd99a" data-itemcategory="normal" data-item-count="1" data-isprimeasin="0" data-giftwrapped="0" data-giftable="0" data-encoded-offering="erF6bSsUaPN0XP13xfXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYvWwmsZJ2HZwKuDJbLvjiR%2BI2CQAPyug7sPmmGV7DdJ"
          data-best-offer-id="erF6bSsUaPN0XP13xfsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXwmsZJ2HZwKuDJbLvjiR%2OezFaQg7sPmmGV7DdJ" data-asin="B0742J1KYD">
          <div class="sc-list-item-spinner" style="display:none;">
            <div class="sc-list-item-overwrap" style="display:none;" />
            <div class="sc-list-item-removed-msg a-padding-medium" style="display:none;">
              <div class="sc-list-item-content">
                <div class="a-row a-spacing-base a-spacing-top-base">
                  <div class="a-column a-span10">
                    <div class="a-fixed-left-grid">
                      <div class="a-fixed-left-grid-inner" style="padding-left:115px">
                        <div class="a-fixed-left-grid-col a-float-left a-col-left" style="width:115px;margin-left:-115px;float:left;">
                          <div class="a-fixed-left-grid-col a-col-right" style="padding-left:0%;float:left;">
                            <ul class="a-unordered-list a-nostyle a-vertical a-spacing-mini">
                              <div class="a-row sc-action-links">
                                <span class="a-size-small sc-action-delete">
<span class="a-declarative" data-sc-item-action="{"itemID":"S2e1cbXXXXXXXXXXXXXXXXXXXXXXXXXXd80b9a","itemType":"saved","isWishListItem":0,"action":"delete","isFresh":0}" data-action="sc-item-action">
<input type="submit" aria-label="Delete MENSI Outdoor Patio Heater M6*0.75 Head Thread With M8X1 End Connection Nuts Thermocouple 410mm" value="Delete" name="submit.delete.S2XXXXXXXXXXXXXXXXXXXXXXXXXX0bd99a"/>
</span>
                                </span>
                                <i class="a-icon a-icon-text-separator" aria-label="|" role="img" />
                                <span class="a-size-small sc-action-add-best-offer sc-invisible-when-no-js">
<input class="wl-refdata" type="hidden" value="true" name="isSelectedForCheckout"/>
<i class="a-icon a-icon-text-separator" aria-label="|" role="img"/>
<span class="a-size-small sc-action-move-to-wishlist sc-invisible-when-no-js">
</div>
</div>
</div>
</div>
</div>
<div class="a-column a-span2 a-text-left a-span-last">
</div>
</div>
</div>
<div class="a-row sc-list-item sc-list-item-border sc-java-remote-feature" data-quantity="8" data-price="7.37" data-outofstock="0" data-minquantity="1" data-itemtype="saved" data-itemislastpantryitem="0" data-itemid="See02cb-XXXXXXXXXXXXXXXXXXXXXXXXXXc5" data-itemcategory="normal" data-item-count="2" data-isprimeasin="0" data-giftwrapped="0" data-giftable="0" data-encoded-offering="MzgFwAMDc3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4bqRzg7IzfFp%2B%2BDg%2BAYyl4X" data-asin="B004DYKIH4">
<div class="sc-list-item-spinner" style="display:none;">
<div class="sc-list-item-overwrap" style="display:none;"/>
<div class="sc-list-item-removed-msg a-padding-medium" style="display:none;">
<div class="sc-list-item-content">
<div class="a-row a-spacing-base a-spacing-top-base">
<div class="a-column a-span10">
<div class="a-fixed-left-grid">
<div class="a-fixed-left-grid-inner" style="padding-left:115px">
<div class="a-fixed-left-grid-col a-float-left a-col-left" style="width:115px;margin-left:-115px;float:left;">
<div class="a-fixed-left-grid-col a-col-right" style="padding-left:0%;float:left;">
<ul class="a-unordered-list a-nostyle a-vertical a-spacing-mini">
<div class="a-row sc-action-links">
<span class="a-size-small sc-action-delete">
<span class="a-declarative" data-sc-item-action="{"itemID":"SeccXXXXXXXXXXXXXXXXXXXXXXXXXXd441c5","itemType":"saved","isWishListItem":0,"action":"delete","isFresh":0}" data-action="sc-item-action">
<input type="submit" aria-label="Delete Bit Adapter - 1/4" to 1/4" - Turn Any Ratchet Into a Driver! Now with Quick-Change By Pro Tools" value="Delete" name="submit.delete.SeccXXXXXXXXXXXXXXXXXXXXXXXXXX5"/>
</span>
                                </span>
                                <i class="a-icon a-icon-text-separator" aria-label="|" role="img" />
                                <span class="a-size-small sc-action-move-to-cart">
<input class="wl-refdata" type="hidden" value="B003IXYJYO" name="creativeAsin"/>
<input class="wl-refdata" type="hidden" value="5BD74XXXXXXXXXXXXXXXXXXXXXXXXXX68" name="assocToken"/>
<input class="wl-refdata" type="hidden" value="xsc" name="linkCode"/>
<input class="wl-refdata" type="hidden" value="true" name="isSelectedForCheckout"/>
<i class="a-icon a-icon-text-separator" aria-label="|" role="img"/>
<span class="a-size-small sc-action-move-to-wishlist sc-invisible-when-no-js">
<i class="a-icon a-icon-text-separator" aria-label="|" role="img"/>
<span id="comparison-lite-modal-B004DYKIH4" class="a-declarative" data-a-modal="{"cache":"0","hideHeader":"true","width":"80%","ajaxFailMsg":"We\u2019re sorry, an error has occurred. Please try again.","url":"/compare/product/B004DYKIH4/ref=psdc_sXXXXB004DYKIH4?viewType=sfl","height":"570"}" data-action="a-modal">
</div>
</div>
</div>
</div>
</div>
<div class="a-column a-span2 a-text-left a-span-last">
</div>
</div>
</div>

根据我在 SO 上阅读的所有内容,上面的 XPath 应该可以工作。是什么导致了这个异常?

谢谢

更新:以下是我所依赖的一些参考资料:

Locating child nodes of WebElements in selenium

WebElement.FindElement(By.XPath) returns element not relative to parent but to the document

How to get all descendants of an element using webdriver?

【问题讨论】:

  • 您提供的 HTML 示例不包含 @id='savedCartViewForm'。但你其余的推理是正确的。
  • @SiKing 我没有包含它,因为这部分用于识别原始列表并且它按预期工作(我验证了它,因为它与页面上的元素数量完全匹配)。我只包括了不工作的部分。我应该更新我的帖子吗?
  • @SiKing 请用savedCartViewForm更新HTML
  • @Sers SiKing:更新

标签: java html selenium selenium-webdriver webdriver


【解决方案1】:

我通过将XPath 更新为:

/descendant::input[@value = 'Delete']

我现在能够识别正确的元素。但是,我仍然不清楚为什么原来的 XPath 不起作用而这个起作用 - 因为根据几篇文章,我读到 '//' 是后代或自我的简写和 .添加,以便搜索相对于父元素开始。所以这个原因对我来说仍然没有意义。

如果有人可以提供带有解释的更新答案,我将选择该答案作为问题的答案,而不是我自己的答案。

【讨论】:

    【解决方案2】:

    用 css 选择器代替 xpath 试试下面的代码。

    List<WebElement> rows = driver.findElements(By.cssSelector("#savedCartViewForm .a-row"));
    rows.forEach(row -> {
        WebElement deleteButton = row.findElement(By.cssSelector("input[value = 'Delete']"));
        System.out.println(deleteButton.getAttribute("aria-label"));
    });
    

    使用 xpath:

    List<WebElement> rows = driver.findElements(By.xpath("//form[@id = 'savedCartViewForm']//div[contains(@class, 'a-row')]"));
    rows.forEach(row -> {
        WebElement deleteButton = row.findElement(By.xpath(".//input[@value = 'Delete']"));
        System.out.println(deleteButton.getAttribute("aria-label"));
    });
    

    【讨论】:

    • 谢谢。是否可以使用 XPath 更新此代码?我的程序是使用 XPath 构建的。您还介意解释为什么我以前的代码不起作用吗?通过这种方式,我了解将来如何修复我的 XPath。非常感谢..
    • @S.O.S 添加了 xpath 选择器
    【解决方案3】:

    这是 Selenium 变得复杂的地方之一。 Xpath 功能取决于几件事。

    1. 有些浏览器支持原生 Xpath,有些则不支持(假设这总是会发生变化)。
    2. 有些驱动程序使用本机浏览器实现,有些则不使用(假设这总是会发生变化)。
    3. 如果由于某种原因不支持原生 Selenium 将回退到 Wicked Good Xpath 支持。

    对于不同的浏览器和不同的驱动程序二进制版本,您现在可能拥有不同的功能。让我们假设所有本机实现都 100% 符合此答案的规范。

    如果您遇到回退到 Wicked Good Xpath 的驱动程序二进制/浏览器版本,它并非完全 100% 符合规范,您可能会看到以下错误:

    https://github.com/google/wicked-good-xpath/issues/43

    现在请记住,Selenium 使用了 Wicked good Xpath 的分叉版本,因此它可能与上面的有所不同。从历史上看 // 在 Selenium 世界中并不意味着后代或自我,它意味着搜索整个 DOM。 Wicked Good Xpath 的当前 Selenium 实现可在以下位置获得:

    https://github.com/SeleniumHQ/selenium/tree/07a18746ff756e90fd79ef253a328bd7dfa9e6dc/third_party/js/wgxpath

    由于您已经确定了问题的解决方案是后裔或自我,但由于速记版本“//”在 Wicked Good XPath 中表示其他含义,因此您没有得到预期的结果。

    TL;DR;

    在为 Selenium 测试构建 Xpath 时不要使用 Xpath 简写。

    【讨论】:

      猜你喜欢
      • 2013-12-28
      • 2022-11-26
      • 2019-02-19
      • 2016-05-11
      • 2021-11-02
      • 2022-12-12
      • 1970-01-01
      • 2013-08-28
      • 2022-01-03
      相关资源
      最近更新 更多