我的公司有一个用于存储活动和旧票务信息的数据库的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 && 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>
答案 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)
})
演示:
参考文献: