繁体   English   中英

在不知道 xpath 的情况下在元素中查找元素?

[英]Finding an element within an element without knowing the xpath?

语境

在尝试单击属于具有特定描述的 GitHub 个人访问令牌 (PAT) 的删除按钮时,在 Python 中使用 Selenium。 我能够找到 PAT 的描述和 ID。 但是,按钮本身不包含对 id 的任何引用。 只有单击按钮后生成的表单才包含该引用。 因此,要了解如何单击右侧按钮,我想我可以在<div id="access-token-836771760" class="access-token js-revoke-item "..元素中找到该按钮。 但是,大多数能够在元素中搜索元素的解决方案都需要知道此条目的 xpath。 我不知道父元素的xpath,因为我是根据token描述找到这个元素的。 显然,一旦您拥有 Selenium 中的元素,获取元素的 xpath 是不切实际的。

HTML 代码

<div class="listgroup">
    <div id="access-token-836771760" class="access-token js-revoke-item " data-id="836771760" data-type="token">
        <div class="listgroup-item">
            <div class="d-flex float-right">

                <details class="ml-2 details-reset details-overlay details-overlay-dark">
                    <summary data-view-component="true" class="btn-danger btn-sm btn" role="button"> Delete
                    </summary>
                    <details-dialog class="anim-fade-in fast Box Box--overlay d-flex flex-column" role="dialog"
                        aria-modal="true">
                        <div class="Box-header">
                            <button class="Box-btn-octicon btn-octicon float-right" type="button"
                                aria-label="Close dialog" data-close-dialog="">
                                <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16"
                                    data-view-component="true" class="octicon octicon-x">
                                    <path fill-rule="evenodd"
                                        d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z">
                                    </path>
                                </svg>
                            </button>
                            <h3 class="Box-title">Are you sure you want to delete this token?</h3>
                        </div>
                        <div data-view-component="true" class="flash flash-warn flash-full">


                            Any applications or scripts using this token will no longer be able to access the GitHub
                            API. You cannot undo this action.



                        </div>
                        <div class="Box-body overflow-auto">
                        </div>
                        <div class="Box-footer">
                            <!-- '"` -->
                            <!-- </textarea></xmp> -->
                            <form class="js-revoke-access-form" data-id="836771760" data-type-name="token"
                                data-turbo="false" action="/settings/tokens/836771760" accept-charset="UTF-8"
                                method="post" style=""><input type="hidden" name="_method" value="delete"
                                    autocomplete="off"><input type="hidden" name="authenticity_token"
                                    value="somevalue">
                                <button type="submit" data-view-component="true" class="btn-danger btn btn-block"> I
                                    understand, delete this token
                                </button>
                            </form>
                        </div>
                    </details-dialog>

                </details>
            </div>

            <small class="last-used float-right">Last used within the last 6 months</small>

            <span class="token-description">
                <strong>
                    <a href="/settings/tokens/836771760" data-pjax="">
                        Set GitHub commit build status values.</a>
                </strong>
                <span class="color-fg-muted">
                    <em>— <span title="Access commit status">repo:status</span></em>
                </span>
            </span>
            <div>
                <span class="color-fg-attention">
                    <a class="color-fg-attention" href="/settings/tokens/836771760/regenerate?index_page=1">
                        Expired <span class="text-semibold text-italic">on Mon, May 2 2022</span>.
                    </a> </span>
            </div>
        </div>
    </div>

    <div id="access-token-826562783" class="access-token js-revoke-item " data-id="826562783" data-type="token">
        <div class="listgroup-item">
            <div class="d-flex float-right">

                <details class="ml-2 details-reset details-overlay details-overlay-dark">
                    <summary data-view-component="true" class="btn-danger btn-sm btn" role="button"> Delete
                    </summary>
                    <details-dialog class="anim-fade-in fast Box Box--overlay d-flex flex-column" role="dialog"
                        aria-modal="true">
                        <div class="Box-header">
                            <button class="Box-btn-octicon btn-octicon float-right" type="button"
                                aria-label="Close dialog" data-close-dialog="">
                                <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16"
                                    data-view-component="true" class="octicon octicon-x">
                                    <path fill-rule="evenodd"
                                        d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z">
                                    </path>
                                </svg>
                            </button>
                            <h3 class="Box-title">Are you sure you want to delete this token?</h3>
                        </div>
                        <div data-view-component="true" class="flash flash-warn flash-full">


                            Any applications or scripts using this token will no longer be able to access the GitHub
                            API. You cannot undo this action.



                        </div>
                        <div class="Box-body overflow-auto">
                        </div>
                        <div class="Box-footer">
                            <!-- '"` -->
                            <!-- </textarea></xmp> -->
                            <form class="js-revoke-access-form" data-id="826562783" data-type-name="token"
                                data-turbo="false" action="/settings/tokens/826562783" accept-charset="UTF-8"
                                method="post"><input type="hidden" name="_method" value="delete"
                                    autocomplete="off"><input type="hidden" name="authenticity_token"
                                    value="someothervalue">
                                <button type="submit" data-view-component="true" class="btn-danger btn btn-block"> I
                                    understand, delete this token
                                </button>
                            </form>
                        </div>
                    </details-dialog>

                </details>
            </div>

            <small class="last-used float-right">Last used within the last 6 months</small>

            <span class="token-description">
                <strong>
                    <a href="/settings/tokens/82653355" data-pjax="">
                        somedescription</a>
                </strong>
                <span class="color-fg-muted">
                    <em>— <span title="something">repo</span></em>
                </span>
            </span>
            <div>
                <span class="color-fg-attention">
                    <a class="color-fg-attention" href="/settings/tokens/826562783/regenerate?index_page=1">
                        Expired <span class="text-semibold text-italic">on Thu, May 19 2022</span>.
                    </a> </span>
            </div>
        </div>
    </div>

</div>

问题

如何使用 Selenium 在 Python 中单击属于access-token-836771760 class 的删除按钮?

方法

我可以找到删除按钮:

danger_button = website_controller.driver.find_elements(By.CSS_SELECTOR,'btn-danger.btn-sm.btn')
        print_attributes_of_elements(danger_button,website_controller)
def print_attributes_of_elements(elements,website_controller):
    for elem in elements:
        attrs = website_controller.driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', elem)
        pprint(attrs)

但是,在这些按钮中,我不知道哪个按钮是正确的。

如果您已经拥有<div id="access-token-836771760" class="access-token js-revoke-item "..元素,它应该很简单:

# get div by description (you already have your div)
div = driver.find_element(By.XPATH, "//a[normalize-space(text())='Test']//ancestor::div[@data-type='token']")

# click delete button
button = div.find_element(By.XPATH, ".//summary")
button.click()

如果您已经拥有对 div 的引用,则无需知道 XPATH。

最后,通过手动分析 xpath 更改模式是什么,我能够获得相对于我知道 xpath 的另一个元素的 xpath。 仍然是一种在元素中查找元素的通用方法,将不胜感激。

这是根据 GitHub 个人访问令牌描述删除已存在的 GitHub 个人访问令牌的经过验证的脚本:

from pprint import pprint
from typing import List
from code.project1.src.Website_controller import Website_controller
from code.project1.src.control_website import click_element_by_xpath, open_url, wait_until_page_is_loaded
from selenium.webdriver.common.by import By

from selenium.webdriver.common.action_chains import ActionChains
from code.project1.src.helper import scroll_shim

def remove_previous_github_pat(hardcoded,website_controller):
    """Assumes the user is logged in into GitHub. Then lists the already 
    existing GitHub personal access token (PAT) descriptions. If the new GitHub
    PAT description is already existing, it deletes the existing GitHub PAT. 
    Then it verifies the GitHub PAT is not yet in GitHub/is removed 
    succesfully."""

    # Check if the token exists, and if yes, get a link containing token id.
    github_pat_exists,link =github_pat_description_exists(hardcoded,website_controller)
    if github_pat_exists:

        # Delete the GitHub personal access token.
        delete_github_pat(link,hardcoded,website_controller)
            
    # Verify token is deleted.
    if github_pat_description_exists(hardcoded,website_controller)[0]:
        raise Exception("Error, GitHub pat is not deleted succesfully.")
        

def github_pat_description_exists(hardcoded,website_controller):
    """Assumes the user is logged in into GitHub. Then lists the already 
    existing GitHub personal access token (PAT) descriptions. If the new GitHub
    PAT description is already existing, it returns True, otherwise returns 
    False. Also returns the url of the GitHub pat that contains the token id."""
    # Go to url containing GitHub pat.
    website_controller.driver = open_url(
        website_controller.driver,
        hardcoded.github_pat_tokens_url,
    )
    # Wait until url is loaded. 
    wait_until_page_is_loaded(6,website_controller)

    # Get the token descriptions through the href element.
    elems = website_controller.driver.find_elements(By.CSS_SELECTOR,f".{hardcoded.github_pat_description_elem_classname} [href]")
    for elem in elems:
        link=elem.get_attribute('href')
        if hardcoded.github_pat_description in elem.text:
            return True, link
    return False, None

            

def delete_github_pat(link,hardcoded,website_controller):
    """Gets the GitHub pat id from the link, then clicks the delete button, and
    the confirm deletion button, to delete the GitHub pat."""
    
    if link[:len(hardcoded.github_pat_tokens_url)] == hardcoded.github_pat_tokens_url:
        github_pat_id=int(link[len(hardcoded.github_pat_tokens_url):])
        print(f'github_pat_id={github_pat_id}')

        # Get the right table row nr.
        valid_indices=list_of_valid_xpath_indices([],f"{hardcoded.github_pat_table_xpath}/div[","]",website_controller)
        row_nr= get_desired_token_index(hardcoded,website_controller,valid_indices)
        
        # Click delete button and deletion confirmation button.
        click_github_pat_delete_button(hardcoded,website_controller,row_nr)
    else:
        raise Exception(f'{link[:len(hardcoded.github_pat_tokens_url)]} is not:{hardcoded.github_pat_tokens_url}')

def list_of_valid_xpath_indices(valid_indices,left,right,website_controller):
    """Returns the row numbers of the GitHub personal access tokens table, 
    starting at index =1. Basically gets how much GitHub pats are stored."""
    if valid_indices == []:
        latest_index=1
    else:
        latest_index=valid_indices[-1]+1

    try:
        row = website_controller.driver.find_element(By.XPATH,
            f"{left}{latest_index}{right}"
            )
        if not row is None:
            print(row.text)
            valid_indices.append(latest_index)
            return list_of_valid_xpath_indices(valid_indices,left,right,website_controller)
        else:
            return valid_indices
    except:
        if len(valid_indices) ==0:
            raise Exception("Did not find any valid indices.")
        return valid_indices

def get_desired_token_index(hardcoded,website_controller,valid_indices:List[int]):
    """Finds the index/row number of the GitHub pat's that corresponds to the 
    description of the GitHub pat that is to be created, and returns this 
    index."""
    for row_nr in valid_indices:
        row_elem = website_controller.driver.find_element(By.XPATH,
            f"{hardcoded.github_pat_table_xpath}/div[{row_nr}]"
            )
        if hardcoded.github_pat_description in row_elem.text:
            return row_nr

def click_github_pat_delete_button(hardcoded,website_controller,row_nr:int):
    """Clicks the delete GitHub pat button, and then clicks the confirm 
    deletion button."""
    delete_button  = website_controller.driver.find_element(By.XPATH,
            f"{hardcoded.github_pat_table_xpath}/div[{row_nr}]/div/div[1]/details/summary"
            )
    delete_button.click()

    confirm_deletion_button = website_controller.driver.find_element(By.XPATH,
            f"{hardcoded.github_pat_table_xpath}/div[{row_nr}]/div/div[1]/details/details-dialog/div[4]/form/button"
            )
    confirm_deletion_button.click()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM