繁体   English   中英

Python中的Selenium,无法单击“按钮”:找不到标记

[英]Selenium in Python, Unable to Click 'button': Tag not found

TL,DR:

无论出于何种原因,我的selenium python脚本似乎无法“点击”我需要的按钮。

语境:

你好。 我的任务是许多人可能熟悉的:我想自动化打开网站,登录,点击网站内的一些下拉菜单链接的过程,最终将我带到一个页面,我可以下载电子表格。 我可以打开网页并登录。要继续,我必须:

  1. 单击下拉菜单标题,然后
  2. 在下拉菜单中,单击相应的选项。

以下是网站相关HTML代码的快照:

<td class="x-toolbar-cell" id="ext-gen45">
    <table id="ext-comp-1045" class="x-btn x-btn-noicon" style="width: auto;" cellspacing="0">
        <tbody class="x-btn-small x-btn-icon-small-left">
            <tr>
                <td class="x-btn-ml"><i>&nbsp;</i></td>
                <td class="x-btn-mc">
                    <em class="x-btn-arrow" unselectable="on">
    <button type="button" id="ext-gen46" class=" x-btn-text">Reports</button>
</em>
                </td>
                <td class="x-btn-mr"><i>&nbsp;</i></td>
            </tr>
        </tbody>
    </table>
</td>

我需要“点击”的项目有一个button标记,具体来说:

<button type="button" id="ext-gen46" class=" x-btn-text">Reports</button>

要用硒选择它,我尝试了以下方法:

reports_click_element = browser.find_element_by_id('ext-gen46').click()

当那次失败时,

reports_element = browser.find_element_by_xpath("//button[contains(text(), 'Reports')]").click()

那个实际执行时没有出现ExceptionMessage错误,但我发现它选择了具有“Reports”文本的页面中的其他元素,而不是我需要的特定按钮。

当我试图点击我需要点击的按钮时,解释器返回一条错误消息,指出无法找到html属性。

我怎么能从这里开始? (我是否应该关注我需要点击按钮上方元素中的不可选unselectable="on"标签?)

如果我可以添加任何问题,请告诉我。 提前致谢。

更新:已经切换到iframe,我相信菜单是其中的一部分 - 但我仍然无法选择按钮。 到目前为止,这是我的Python代码:

from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
import time

binary = FirefoxBinary('C:\Program Files (x86)\Mozilla Firefox\Firefox.exe')
browser = webdriver.Firefox(firefox_binary=binary)

browser.get("https://app.website.com")

login_entry(username, password) # this works fine; it's just a user-created function to login. Ignore.

time.sleep(10) # wait for website's markup to load
browser.switch_to.frame(browser.find_element_by_tag_name("iframe"))
time.sleep(10)

# This is the point where I'm trying to click on the "Reports" button
reports_element = browser.find_element_by_xpath("//*[contains(text(), 'Reports')]") #this refers to other elements
reports_element = browser.find_element_by_xpath("//button[contains(text(), 'Reports')][1]") #no luck here either

“页面源”仅是文档请求附带的内容。 在页面加载后,它不会显示通过javascript创建的任何DOM元素。 听起来你的元素听起来像某种iframe。 在浏览器控制台中,尝试此操作并查看它是否返回任何元素:

document.querySelectorAll('iframe')

编辑您的更新:

再次,页面源仅是文档加载时可用的页面源。 之后发生的所有动态加载只能通过使用浏览器检查器或通过使用javascript获取文档的一部分来查看。 链接基本上是相同的html可能是因为它是一个与javascript一起工作的链接,并不意味着导致实际的html文档页面。 您可能需要在代码中执行以下操作:

browser.switch_to.frame(browser.find_element_by_css_selector('iframe'))
reports_element = browser.find_element_by_link_text('Reports')
reports_element.click()

想到了几个案例。

存在多个元素,但并非所有元素都可见

elements = browser.find_elements_by_xpath("//button[contains(text(), 'Reports')]")
for element in elements:

    if element.is_displayed():
        print "element is visible"
        element.click()
    else:
        print("element is not visible")
        print(element)

该元素存在,它将是可见的但是在屏幕之外。

from selenium.webdriver.common.action_chains import ActionChains
elements = browser.find_elements_by_xpath("//button[contains(text(), 'Reports')]")
for element in elements:  
    ActionChains(driver).move_to_element(element).perform()
    try:
        element.click()
    except:
        print("couldn't click on {}".format(element)) 

您还可以尝试使用Selenium IDE for Firefox记录您的点击和键盘条目吗? 然后将其保存为python脚本并将其作为注释发布在这里?

我建议尝试以下事项:

  1. 切换到正确的框架。 可能no frameone framemore than one nested frames ,其中您的元素可以作为另一个more than one nested frames的子元素。 因此,您必须在找到元素之前切换到right frame 我的详细答案就在这里。

  2. 使用更受限制的XPATH。 例如:

report_element =browser.find_element_by_xpath("//td/em/button[text()='Reports']")

一些想法:

打印iframe的DOM源代码并检查html是否符合您的预期:

from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
import time

binary = FirefoxBinary('C:\Program Files (x86)\Mozilla Firefox\Firefox.exe')
browser = webdriver.Firefox(firefox_binary=binary)

browser.get("https://app.website.com")

login_entry(username, password) # this works fine; it's just a user-created function to login. Ignore.

time.sleep(10) # wait for website's markup to load
browser.switch_to.frame(browser.find_element_by_tag_name("iframe"))
time.sleep(10)

print browser.page_source

例如,如果页面中有多个iframe ,则可能会切换到错误的iframe

如果html是正确的,那么你可以尝试使用按钮的id而不是试图通过文本获取它:

reports_element = browser.find_element_by_xpath("//button[@id='ext-gen46']")

如果仍然无效,您可以尝试使用javascript单击按钮:

browser.execute_script("""
    (function() {
        document.getElementById("ext-gen46").click();
    })()
""")

您甚至可以通过将jQuery保存在本地文件中,然后将内容存储到变量并运行来包含jQuery,如:

with open(JQUERY_PATH) as f:
    jquery = f.read()

browser.execute_script(jquery)

然后你可以用它点击按钮:

driver.execute_script("""
    (function() {
        jQuery(document).ready(function($) {
            $('#ext-gen46').click();
        }, jQuery)
    })()
""")

我正在做与Siebel OneView类似的事情,它将大部分控制按钮和菜单放在Java屏幕元素中。 这些元素阻止我找到用Selenium激活的HTML对象,所以我又回到了使用pyautogui

import pyautogui
import time
#look for and click the Activites menu
try:
    x,y = pyautogui.locateCenterOnScreen('acts_button.png')
    try:
        pyautogui.click(x, y)
    except PermissionError:
        #allow load - note this is AFTER the click permission error
        time.sleep(7)
        pass
except TypeError:
    <code to deal with NOT finding the button>

这将查找以前拍摄的屏幕截图的副本,该副本存储在与python脚本相同的文件位置中。 在本节中,图像称为acts_button.png

[快速说明:有两个try: except:这里的陈述。 内部处理单击按钮的任何问题,因为Windows通常会抛出权限错误。 外部更重要,并告诉您的脚本如果找不到按钮该怎么做。 在我的代码中,我尝试使用pyautogui.click(958, 169)点击预设的x,y位置; 如果失败,我要求用户输入(下一个屏幕检测到的失败未加载)。

屏幕截图本身是使用这样的命令创建的

 acts_button = pyautogui.screenshot('acts_button.png', region=(928,162,63,15))

region是目标按钮的以下部分的元组,以像素为单位

  • 左手边缘
  • 顶边
  • 宽度
  • 高度

现在您只需要一种方法来查找截取屏幕截图时的(x,y)值。 幸运的是我也有代码:

#! python3
import pyautogui, sys, time
print('Press Ctrl-C to quit.')
try:
    while True:
        x, y = pyautogui.position()
        positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4)
        print(positionStr, end='')
        print('\b' * len(positionStr), end='', flush=True)
        time.sleep(1)        
except KeyboardInterrupt:
    print('\n')

这将弹出一个带有鼠标(x,y)坐标的小窗口,每秒更新一次。

因此,设置新的单击事件的例程

  1. 打开要导航的站点
  2. 使用鼠标定位器脚本查找屏幕截图元组的设置
  3. 使用pyautogui.screenshot保存按钮的图像
  4. 使用主脚本查找并单击该图像

经过几次,你应该可以在不到一分钟的时间内设置一个新的点击事件。 您可以使用此方法,你知道硒会失败,或捕捉和管理异常时硒可能会失败的一种方式。

您应该检查当前页面上是否只有一个iframe 您可以使用

len(browser.find_elements_by_tag_name('iframe'))

如果输出大于1并且您使用browser.switch_to.frame(browser.find_element_by_tag_name("iframe")) ,则表示您尝试切换到webdriver找到的第一个iframe ,但实际上您可能需要切换到另一个iframe

好的解决方案是使用F12在按钮的祖先中找到所需的iframe ,或者右键单击按钮+ inspect element并在XPath中使用iframe特定属性来匹配精确的iframe ,例如

browser.switch_to.frame(browser.find_element_by_xpath('//iframe[@class="iframe_class_name"]'))

糟糕的解决方案是通过穷举搜索来定义所需iframe的索引,例如:

browser.switch_to.frame(browser.find_elements_by_tag_name("iframe")[0])
reports_element = browser.find_element_by_xpath("//button[text()='Reports'][contains(@id, 'ext-gen')]")
reports_element.click()
...
browser.switch_to.frame(browser.find_elements_by_tag_name("iframe")[1])
reports_element = browser.find_element_by_xpath("//button[text()='Reports'][contains(@id, 'ext-gen')]")
reports_element.click()
....

为什么不使用python 请求 (带会话)和BeautifulSoup模块来完成这种工作(用户在网站上进行交互)?

暂无
暂无

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

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