使用 Selenium 登录站点时出现问题

Problems logging in to site with Selenium

我正在尝试学习如何使用 Selenium 登录 site:Ingram-Micro。我制作了一个脚本,它在不同的页面上运行:https://news.ycombinator.com/login.

现在我正尝试将同样的东西应用到 Ingram-Micro,但我被卡住了,我不知道还能尝试什么。我遇到的问题是 error/message 表示提交元素不可点击,页面底部有一个接受 cookie 按钮似乎是导致问题的原因。

我试图解释它,但我总是收到错误消息,指出该元素不存在。但是,如果我不尝试单击接受 cookie 元素,我会收到原始错误,指出提交按钮不可单击。这是我的代码:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException 
import time

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')

url = "https://usa.ingrammicro.com/_layouts/CommerceServer/IM/Login.aspx? 
returnurl=//usa.ingrammicro.com/"
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)

def login():
    USERNAME = 'email'
    PASSWORD = 'password'         
    element = driver.find_element_by_link_text('I ACCEPT')
    if element.is_displayed():
        print("Element found")
        element.click()
    else:
        print("Element not found")

driver.find_element_by_id('okta-signin-username').send_keys(USERNAME)
driver.find_element_by_id('okta-signin-password').send_keys(PASSWORD)
driver.find_element_by_id('okta-signin-submit').click()      

login()

try:   
    me = driver.find_element_by_id("login_help-about")  
    print(f"{me.text} Element found")

except NoSuchElementException:
    print('Not found')

driver.quit()

这是我得到的错误:

selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element <input class="button button-primary" type="submit" value="Log in" id="okta-signin-submit" data-type="save"> is not clickable at point (365, 560). Other element would receive the click: <p class="cc_message">...</p>

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate 
element: {"method":"link text","selector":"I ACCEPT"}
(Session info: headless chrome=84.0.4147.125)

您可能需要点击输入上方的div。尝试这样的事情:

child = driver.find_element_by_id('okta-signin-submit')
parent = child.find_element_by_xpath('..') # get the parent
parent.click() # click parent element

更新:这在没有无头的 geckodrive 上效果很好,但在 chromedrive 上效果不佳。所以我尝试了别的东西。不要单击按钮,只需在表单中按回车键并以这种方式提交:

from selenium.webdriver.common.keys import Keys
...
driver.find_element_by_id('okta-signin-username').send_keys(USERNAME)
password_field = driver.find_element_by_id('okta-signin-password')
password_field.send_keys(PASSWORD)
password_field.send_keys(Keys.RETURN)

您面临的挑战是围绕脚本进行同步。

此站点上的事件链是 1) 页面已加载,2) 启动 javascript,3) 将 cookie window 滑入视图...

但是,在页面加载后,selenium 不知道脚本,所以它认为可以继续。它试图在按钮出现之前点击它,但因为找不到它而感到沮丧。 (NoSuchElementException)

有不同的同步策略 - 这里的工作是 webdriverwait 告诉 selenium 等待(没有错误)直到你的对象达到指定的预期条件。

您可以阅读有关等待和预期条件的更多信息here

试试这个代码。 对于 cookie“我接受”按钮,我将标识符更改为 xpath(因为我喜欢 xpaths)并将其包装在 webdriverwait 中,等待对象可点击...

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException 
import time

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By


chrome_options = Options()
#chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')

url = "https://usa.ingrammicro.com/_layouts/CommerceServer/IM/Login.aspx?returnurl=//usa.ingrammicro.com/"
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)

def login():
    USERNAME = 'email'
    PASSWORD = 'password'  

    element = WebDriverWait(driver, 30).until(EC.element_to_be_clickable((By.XPATH, '//a[text()="I ACCEPT"]')))       
    if element.is_displayed():
        print("Element found")
        element.click()
    else:
        print("Element not found")

    driver.find_element_by_id('okta-signin-username').send_keys(USERNAME)
    driver.find_element_by_id('okta-signin-password').send_keys(PASSWORD)
    driver.find_element_by_id('okta-signin-submit').click()      

login()

请注意,我必须删除 headless 以检查它是否有效,并且顶部还有 3 个额外的导入。


当您没有很多复杂的对象,或者具有不同等待条件的对象时,Webdriverwait 非常有用。

另一种同步和(在我的观点中更容易)是在脚本开始时设置一个隐式等待 ONCE - 这会配置驱动程序对象。

driver.implicitly_wait(10)

正如前面 link 所说:

An implicit wait tells WebDriver to poll the DOM for a certain amount of time when trying to find any element (or elements) not immediately available. The default setting is 0. Once set, the implicit wait is set for the life of the WebDriver object.

您可以像这样使用它 .. 不执行所有代码,只需在创建驱动程序和代码工作后添加这一行:

.....
url = "https://usa.ingrammicro.com/_layouts/CommerceServer/IM/Login.aspx?returnurl=//usa.ingrammicro.com/"
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
driver.implicitly_wait(10) # seconds

def login():
    USERNAME = 'email'
    PASSWORD = 'password'  

    element = driver.find_element_by_link_text('I ACCEPT')
    if element.is_displayed():
        print("Element found")
        element.click()
    else:
        print("Element not found")
........