[英]Selenium in Python, Unable to Click 'button': Tag not found
TL,DR:
无论出于何种原因,我的selenium python脚本似乎无法“点击”我需要的按钮。
语境:
你好。 我的任务是许多人可能熟悉的:我想自动化打开网站,登录,点击网站内的一些下拉菜单链接的过程,最终将我带到一个页面,我可以下载电子表格。 我可以打开网页并登录。要继续,我必须:
以下是网站相关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> </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> </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脚本并将其作为注释发布在这里?
我建议尝试以下事项:
切换到正确的框架。 可能no frame
或one frame
或more than one nested frames
,其中您的元素可以作为另一个more than one nested frames
的子元素。 因此,您必须在找到元素之前切换到right frame
。 我的详细答案就在这里。
使用更受限制的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)坐标的小窗口,每秒更新一次。
因此,设置新的单击事件的例程
pyautogui.screenshot
保存按钮的图像 经过几次,你应该可以在不到一分钟的时间内设置一个新的点击事件。 您可以使用此方法,你知道硒会失败,或捕捉和管理异常时硒可能会失败的一种方式。
您应该检查当前页面上是否只有一个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.