使用Selenium(Python)在网页上查找按钮时遇到动态(更改)XPath的麻烦

时间:2019-08-27 19:55:36

标签: python selenium xpath

我的公司有一个用于存储活动和旧票务信息的数据库的Web UI。我正在尝试通过使用硒通过Web UI刮取票证数据来自动化每天更新活动票证的过程。我遇到了动态选择器的问题。

我目前在我的xpath中使用“包含”功能,而xpath中的某些部分是恒定的,但是除非我对动态部分进行硬编码,否则我会得到一个错误,即“元素不可交互”。参见代码

#login
driver.get(url)
driver.maximize_window()
driver.find_element_by_name("username").send_keys(username)
driver.find_element_by_name("password").send_keys(password)
driver.find_element_by_xpath("//button[1]").click()
time.sleep(2)

#buttons that we need to find on the page
#xPath for optionsButton ***DYNAMIC***
#//*[@id="1563584724507-0-uiGrid-001L-cell"]/md-menu/button
#//*[@id="1566933317404-0-uiGrid-00JG-cell"]/md-menu/button
#xPath for detailButton ***DYNAMIC***
#//*[@id="menu_container_3"]/md-menu-content/md-menu-item[1]/button
#//*[@id="menu_container_18"]/md-menu-content/md-menu-item[1]/button

#submit ticket number 
#oldTickets = list of tickets from old handoff
#newTickets = list of tickets from alert emails
def getDetail(list):
    dict = {}
    optionsButton = "//*[contains(@id,'0-uiGrid-00')]/md-menu/button"
    detailButton = "//*[contains(@id,'menu_container_')]/md-menu-content/md-menu-item[1]/button"
    for item in list:
        driver.find_element_by_id("keyword").clear()
        driver.find_element_by_id("keyword").send_keys(item)
        driver.find_element_by_id("keyword").send_keys(Keys.ENTER)
        time.sleep(2)
        #WebDriverWait(driver,10).until(EC.presence_of_element_located(driver.find_element_by_xpath(optionsButton)))
        driver.find_element_by_xpath(optionsButton).click()
        time.sleep(2)
        #WebDriverWait(driver,10).until(EC.presence_of_element_located(driver.find_element_by_xpath(detailButton)))
        driver.find_element_by_xpath(detailButton).click()
        time.sleep(5)
        #grab ticket notes from element
        #xpath of container storing ticketData //*[@id="ticketNotes"]
        ticketDetail = driver.find_element_by_id("ticketNotes").text
        dict[item] = ticketDetail
    return dict

尝试创建此函数以返回字典,其中:

dict = { ticket_number:ticket_detail ... }

更新:

这是给我问题的按钮的外部html

<button class="md-button md-ink-ripple" type="button" ng-transclude="" ng-click="grid.appScope.openItemTab(row.entity.TicketId)" ng-disabled="grid.appScope.disabledDetailBtns[row.entity.TicketId]" role="menuitem">              <i class="fa fa-info-circle ng-scope" style="margin:auto 16px auto 0;"></i><span class="ng-scope"> Ticket Detail          </span></button>

网格行:

<div role="row" ui-grid-row="row" row-render-index="rowRenderIndex" class="ng-isolate-scope"><div ng-dblclick="grid.appScope.openItemTab(row.entity.TicketId)" layout-gt-sm="row" class="ng-scope layout-gt-sm-row" tabindex="0">  <!-- ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003I rowID layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003I-cell"><div class="ui-grid-cell-contents ng-binding ng-scope">16852369 <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.TicketId)" role="button" tabindex="0">copy</span><span hide-gt-md="" class="hide-gt-md ng-binding"> - </span><span flex="" class="flex"></span><i class="fa fa-info-circle hide-gt-md" hide-gt-md="" ng-click="grid.appScope.openItemTab(row.entity.TicketId)" ng-disabled="grid.appScope.disabledDetailBtns[row.entity.ticketId]" aria-label="Ticket Detail" role="button" tabindex="0" aria-disabled="false"></i></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003J layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003J-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Severity: </strong>Medium <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.Severity)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003K layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003K-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Status: </strong>Service Restored <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.Status)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003L layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003L-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Condition: </strong>Open-Dispatch <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.Condition)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003M layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003M-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Case Type: </strong>MACD <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.CaseType)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003N layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003N-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Owner: </strong>Enrique Covarrubias <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.Owner)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003O responsiveHide layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003O-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Title: </strong>AHT // 08/06/19 - 0300 GMT // Circuit that needs the change <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.Title)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003P layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003P-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Customer: </strong>Interactive Intelligence, Inc. <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.Customer.CustomerName)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003Q layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003Q-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Queue: </strong>NA - Ops <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.QueueName)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003R layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003R-cell"><div class="ui-grid-cell-contents ng-binding ng-scope"><strong>Create Date: </strong>2019-08-05T12:33:31Z <span class="copybtn" ng-click="grid.appScope.copytext(row.entity.CreationTime)" role="button" tabindex="0">copy</span></div></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --><div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell ng-scope ui-grid-coluiGrid-003S layout-align-start-center layout-row" ng-class="{ 'ui-grid-row-header-cell': col.isRowHeader }" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" ui-grid-cell="" layout="row" layout-align="start center" tabindex="-1" id="1567012246510-0-uiGrid-003S-cell"><md-menu md-position-mode="target-right target" class="md-menu ng-scope _md">   <button class="md-icon-button md-raised md-primary md-button md-ink-ripple" type="button" ng-transclude="" aria-label="Options for " ng-click="grid.appScope.search.openResultMenu($mdMenu,$event,'Create Subcase')" aria-haspopup="true" aria-expanded="false" aria-owns="menu_container_5">      <i class="fa fa-ellipsis-h ng-scope" md-menu-origin=""></i>   <div class="md-ripple-container"></div></button>   <div class="_md md-open-menu-container md-whiteframe-z2" id="menu_container_5" style="display: none;" aria-hidden="true"><md-menu-content role="menu">      <md-menu-item>          <button class="md-button md-ink-ripple" type="button" ng-transclude="" ng-click="grid.appScope.openItemTab(row.entity.TicketId)" ng-disabled="grid.appScope.disabledDetailBtns[row.entity.TicketId]" role="menuitem">              <i class="fa fa-info-circle ng-scope" style="margin:auto 16px auto 0;"></i><span class="ng-scope"> Ticket Detail          </span></button>      </md-menu-item>      <!-- ngIf: grid.appScope.ticketIsClosed(row.entity.Condition) --><md-menu-item ng-if="grid.appScope.ticketIsClosed(row.entity.Condition)" class="ng-scope">          <button class="md-button md-ink-ripple" type="button" ng-transclude="" ng-click="grid.appScope.search.ticketAction('Order Parts', row.entity, 'search')" ng-disabled="grid.appScope.disabledDetailBtns[row.entity.TicketId]" role="menuitem">              <i class="fa fa-laptop ng-scope" style="margin:auto 16px auto 0;"></i><span class="ng-scope"> Order Parts          </span></button>      </md-menu-item><!-- end ngIf: grid.appScope.ticketIsClosed(row.entity.Condition) -->      <!-- ngIf: row.entity.TicketId.indexOf('-') < 0 && row.entity.Condition !== 'Closed' --><md-menu-item ng-if="row.entity.TicketId.indexOf('-') < 0 &amp;&amp; row.entity.Condition !== 'Closed'" class="ng-scope">           <button class="md-button md-ink-ripple" type="button" ng-transclude="" ng-click="grid.appScope.search.ticketAction('Create Subcase',row.entity,'search')" role="menuitem">               <md-icon md-svg-src="images/_icons/subcase.svg" style="width:16px;height:16px;min-width:16px;min-height:16px;fill:#777;" class="ng-scope" aria-hidden="true"><svg version="1.1" id="Layer_2_cache6" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 72 72" style="enable-background:new 0 0 72 72;" xml:space="preserve" fit="" height="100%" width="100%" preserveAspectRatio="xMidYMid meet" focusable="false">
<style type="text/css">
    .st0{fill:none;stroke:#000000;stroke-width:4;stroke-miterlimit:10;}
</style>
<path class="st0" d="M16.3,20.8c0,0-5.2,0-9,0S2,23.6,2,26s0,30,0,32.9s1.9,3.6,3.6,3.6s34,0,36.9,0c2.9,0,5.7-1.7,7.6-3.6
    s7.6-8.6,9.5-11.4c1.9-2.9,0-5.7-2.9-5.7s-32.4,0-35.2,0s-5.7,1-7.6,2.9c-1.9,1.9-10,11.9-10,11.9c-1.9,2.9-1,4.8,0,5.7"></path>
<path d="M19.1,36.5c0,0,0-17.1,0-21.9S22,6,26.5,6c3.8,0,5.4,0,9.2,0c4.8,0,7.3,3.8,7.3,6.7s0,1.9,0,1.9s18.1,0,21.9,0
    c3.8,0,7.1,2.9,7.1,7.1c0,5.2,0,21.4,0,24.3s-3.8,5.5-5.5,5.5c-1.7,0-3.9,0-3.9,0s5.2-3.5,3-9.1c-2.6-6.4-10.4-5.9-10.4-5.9H19.1z"></path>
</svg></md-icon><span class="ng-scope"> Create Subcase           </span></button>      </md-menu-item><!-- end ngIf: row.entity.TicketId.indexOf('-') < 0 && row.entity.Condition !== 'Closed' -->      <!-- ngIf: row.entity.TicketId.indexOf('-') < 0 --><md-menu-item ng-if="row.entity.TicketId.indexOf('-') < 0" class="ng-scope">           <button class="md-button md-ink-ripple" type="button" ng-transclude="" ng-click="grid.appScope.search.ticketAction('Create Follow-up',row.entity,'search')" role="menuitem">               <md-icon md-svg-src="images/_icons/subcase.svg" style="width:16px;height:16px;min-width:16px;min-height:16px;fill:#777;" class="ng-scope" aria-hidden="true"><svg version="1.1" id="Layer_2_cache7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 72 72" style="enable-background:new 0 0 72 72;" xml:space="preserve" fit="" height="100%" width="100%" preserveAspectRatio="xMidYMid meet" focusable="false">
<style type="text/css">
    .st0{fill:none;stroke:#000000;stroke-width:4;stroke-miterlimit:10;}
</style>
<path class="st0" d="M16.3,20.8c0,0-5.2,0-9,0S2,23.6,2,26s0,30,0,32.9s1.9,3.6,3.6,3.6s34,0,36.9,0c2.9,0,5.7-1.7,7.6-3.6
    s7.6-8.6,9.5-11.4c1.9-2.9,0-5.7-2.9-5.7s-32.4,0-35.2,0s-5.7,1-7.6,2.9c-1.9,1.9-10,11.9-10,11.9c-1.9,2.9-1,4.8,0,5.7"></path>
<path d="M19.1,36.5c0,0,0-17.1,0-21.9S22,6,26.5,6c3.8,0,5.4,0,9.2,0c4.8,0,7.3,3.8,7.3,6.7s0,1.9,0,1.9s18.1,0,21.9,0
    c3.8,0,7.1,2.9,7.1,7.1c0,5.2,0,21.4,0,24.3s-3.8,5.5-5.5,5.5c-1.7,0-3.9,0-3.9,0s5.2-3.5,3-9.1c-2.6-6.4-10.4-5.9-10.4-5.9H19.1z"></path>
</svg></md-icon><span class="ng-scope"> Create Follow-up           </span></button>      </md-menu-item><!-- end ngIf: row.entity.TicketId.indexOf('-') < 0 -->      <!-- ngIf: grid.appScope.ticketIsClosed(row.entity.Condition) && grid.appScope.userPermissionToClose() -->      <!-- ngIf: !grid.appScope.ticketIsClosed(row.entity.Condition) -->   </md-menu-content></div></md-menu></div><!-- end ngRepeat: (colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name --></div></div>

1 个答案:

答案 0 :(得分:0)

如果要匹配具有子元素<button><span>元素,而子元素Ticket Details则包含文本//button[normalize-space()='Ticket Detail'] ,最简单的方法是使用normalize-space()函数,例如:

def test = udf(() => {
  def a =  Map("A" -> "A")
  def b =  Map("B" -> "B")

  List(a, b)
})

演示:

enter image description here

参考文献: