1. 程式人生 > >1113Selenium web自動化測試經驗分享-frame切換時的定位、expected_conditions模組的frame_to_be_available_and_switch_to_it()

1113Selenium web自動化測試經驗分享-frame切換時的定位、expected_conditions模組的frame_to_be_available_and_switch_to_it()

這篇分享裡面坑很多,不推薦掌握;我是爬了又掉,接著爬,接著掉坑,到現在總算是有點收穫。

一)frame表單切換時的定位方法

大家都知道,對於不同的iframe/frame表單中的元素是無法直接定位的。需要先結合switchTo().frame()方法切換到指定的frame/iframe中。

關於定位方法,個人建議 2點:driver.switch_to.frame(xxxx) xxxx 可以是id屬性值;或是查詢到這個元素(WebElement)。
可能很多人會說 name屬性也是可以用的,但為了方便記憶後面的frame_to_be_available_and_switch_to_it()的用法,就比較推薦那id和WebElement來定位。

關於郵箱登入時iframe的切換,有些網站(126郵箱、Yeah.net網易免費郵)和163的是類似,動態id;有些網站是沒有設定iframe,可以直接定位到輸入框;還有些是和QQ郵箱(189郵箱、21CN個人郵箱、阿里郵箱)類似,id是固定屬性值;

    def test_57o1(self):
        """expected_conditions模組下的frame_to_be_available_and_switch_to_it()"""
        # frame的定位 driver.switch_to.frame(xxxx)   xxxx 可以是id屬性值;或是查詢到這個元素WebElement,不可以使用locator元組

        driver = webdriver.Chrome()
        driver.maximize_window()
        driver.get('https://mail.163.com/')

        # <iframe name="" frameborder="0" id="x-URS-iframe1542168428186.7546" scrolling="no" style="width: 100%; height: 100%; border: none; background: none;" src="https://dl.reg.163.com/webzj/v1.0.1/pub/index_dl2_new.html?cd=https%3A%2F%2Fmimg.127.net%2Findex%2F163%2Fscripts%2F2017%2Fpc%2Fcss%2F&amp;cf=urs.7ac8b88e.css&amp;MGID=1542168428186.7546&amp;wdaId=&amp;pkid=CvViHzl&amp;product=mail163"></iframe>

        frame1 = self.xin_find_element(driver, By.CSS_SELECTOR, 'iframe[frameborder="0"]')    # 查詢到這個元素WebElement,通過
        # frame1 = 'id'     # 無法嘗試 動態id

        driver.switch_to.frame(frame1)
        self.xin_find_element(driver, By.CSS_SELECTOR, 'input[placeholder="郵箱帳號或手機號碼"]').clear()
        self.xin_find_element(driver, By.CSS_SELECTOR, 'input[placeholder="郵箱帳號或手機號碼"]').send_keys('185')

        time.sleep(1)
        driver.quit()

上圖程式碼是處理 163郵箱登入介面iframe:動態id,推薦WebElement來定位。

    def test_57o3(self):
        """expected_conditions模組下的frame_to_be_available_and_switch_to_it()"""
        # frame的定位 driver.switch_to.frame(xxxx)   xxxx 可以是id屬性值;或是查詢到這個元素WebElement,不可以使用locator元組

        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(10)
        self.driver.get("https://mail.qq.com/")

        # <iframe id="login_frame" name="login_frame" height="100%" scrolling="no" width="100%" frameborder="0" src="https://xui.ptlogin2.qq.com/cgi-bin/xlogin?target=self&amp;appid=522005705&amp;daid=4&amp;s_url=https://mail.qq.com/cgi-bin/readtemplate?check=false%26t=loginpage_new_jump%26vt=passport%26vm=wpt%26ft=loginpage%26target=&amp;style=25&amp;low_login=1&amp;proxy_url=https://mail.qq.com/proxy.html&amp;need_qr=0&amp;hide_border=1&amp;border_radius=0&amp;self_regurl=http://zc.qq.com/chs/index.html?type=1&amp;app_id=11005?t=regist&amp;pt_feedback_link=http://support.qq.com/discuss/350_1.shtml&amp;css=https://res.mail.qq.com/zh_CN/htmledition/style/ptlogin_input24e6b9.css"></iframe>

        # frame11 = "login_frame"       # 通過 但是無法確定是id 還是name屬性值來做的判斷
        # frame11 = (By.XPATH, '//iframe[@frameborder="0"]')      # 報錯 Message: unknown error: invalid 'id'
        frame11 = self.driver.find_element(By.XPATH, '//iframe[@frameborder="0"]')      # 查詢到這個元素WebElement 通過
        self.driver.switch_to.frame(frame11)

        self.driver.find_element_by_id("switcher_plogin").click()   # 賬號密碼登入
        self.driver.find_element_by_id("u").clear()
        self.driver.find_element_by_id("u").send_keys('987654')

        time.sleep(1)
        self.driver.quit()

上圖程式碼是處理 QQ郵箱登入介面的iframe:推薦id定位和WebElement。

二)expected_conditions模組下的frame_to_be_available_and_switch_to_it()

expected_conditions模組下有個類frame_to_be_available_and_switch_to_it(),主要是用來判斷該frame是否可以switch進去。
單獨使用:可傳入定位方式id屬性值,成功的話就返回True並switch進去,否則 返回False;也可傳入locator元組或WebElement,成功定位的話就返回True並switch進去,否則 會報錯,找不到定位。

    def test_57o4(self):
        """expected_conditions模組下的frame_to_be_available_and_switch_to_it()"""
        # frame_to_be_available_and_switch_to_it() 判斷該frame是否可以switch進去
        # 如果直接傳入定位方式id,可以的話就返回True並switch進去,否則返回False
        # 也可傳入locator元組或WebElement,可以的話就返回True並switch進去,否則報錯,找不到定位

        from selenium.webdriver.support.wait import WebDriverWait
        from selenium.webdriver.support import expected_conditions as EC
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(5)
        self.driver.get("https://mail.qq.com/")
        print('0', time.ctime())
        # <iframe id="login_frame" name="login_frame" height="100%" scrolling="no" width="100%" frameborder="0" src="https://xui.ptlogin2.qq.com/cgi-bin/xlogin?target=self&amp;appid=522005705&amp;daid=4&amp;s_url=https://mail.qq.com/cgi-bin/readtemplate?check=false%26t=loginpage_new_jump%26vt=passport%26vm=wpt%26ft=loginpage%26target=&amp;style=25&amp;low_login=1&amp;proxy_url=https://mail.qq.com/proxy.html&amp;need_qr=0&amp;hide_border=1&amp;border_radius=0&amp;self_regurl=http://zc.qq.com/chs/index.html?type=1&amp;app_id=11005?t=regist&amp;pt_feedback_link=http://support.qq.com/discuss/350_1.shtml&amp;css=https://res.mail.qq.com/zh_CN/htmledition/style/ptlogin_input24e6b9.css"></iframe>

        # 直接傳入定位方式id
        # print(EC.frame_to_be_available_and_switch_to_it('login_frame')(self.driver))        # 正確的 返回True並switch進去
        # print(EC.frame_to_be_available_and_switch_to_it('login_frame123')(self.driver))     # 錯誤的 返回False

        # 傳入locator元組
        # print(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe[frameborder="0"][width="100%"]'))(self.driver))       # 正確的 返回True並switch進去
        # print(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe[frameborder="10"]'))(self.driver))    # 報錯 Message: no such element

        # 傳入WebElement
        abcd = self.driver.find_element(By.XPATH, '//iframe[@height="100%" and @scrolling="no"]')       # 正確的 返回True並switch進去
        # abcd = self.driver.find_element(By.XPATH, '//iframe[@height="1000%" and @scrolling="no123"]')       # 報錯 Message: no such element
        print(EC.frame_to_be_available_and_switch_to_it(abcd)(self.driver))

        self.driver.find_element_by_id("switcher_plogin").click()
        self.driver.find_element_by_id("u").clear()
        self.driver.find_element_by_id("u").send_keys('987654')
        self.driver.switch_to.default_content()
        print('1', time.ctime())

        time.sleep(1)
        self.driver.quit()
class frame_to_be_available_and_switch_to_it(object):
    """ An expectation for checking whether the given frame is available to
    switch to.  If the frame is available it switches the given driver to the
    specified frame.
    """
    def __init__(self, locator):
        self.frame_locator = locator

    def __call__(self, driver):
        try:
            if isinstance(self.frame_locator, tuple):
                driver.switch_to.frame(_find_element(driver,
                                                     self.frame_locator))
            else:
                driver.switch_to.frame(self.frame_locator)
            return True
        except NoSuchFrameException:
            return False

原始碼沒怎麼看懂 = =

分享下 自己琢磨出來的經驗:

frame_to_be_available_and_switch_to_it()和顯式等待結合後,傳入定位方式id、locator元組,可以的話就返回True並switch進去,否則 超時報錯 selenium.common.exceptions.TimeoutException;傳入WebElement,可以的話就返回True並switch進去,否則找不到定位報錯 selenium.common.exceptions.NoSuchElementException: Message: no such element。
(報錯型別不同)

    def test_57o7(self):
        """expected_conditions模組下的frame_to_be_available_and_switch_to_it()"""

        # frame_to_be_available_and_switch_to_it() 判斷該frame是否可以switch進去
        # 如果直接傳入定位方式id,可以的話就返回True並switch進去,否則返回False
        # 也可傳入locator元組或WebElement,可以的話就返回True並switch進去,否則報錯,找不到定位

        # 和顯式等待結合後,傳入定位方式id、locator元組,可以的話就返回True並switch進去,否則 超時報錯 selenium.common.exceptions.TimeoutException
        # 和顯式等待結合後,傳入WebElement,可以的話就返回True並switch進去,否則報 找不到定位報錯 selenium.common.exceptions.NoSuchElementException: Message: no such element

        # 此外 WebElement因為是去定位某個元素,定位不到會報錯,應該放在try裡面,放在try前面 會執行到那一步就直接報錯;
        # 定位某個元素的WebElement放在try裡面,但又用不到那個顯式等待的!在執行前就已經報錯 找不到元素。
        # 對比 錯誤的locator元組(放在裡面、外面都可以,因為不查詢)是用的當前的顯式等待!  建議都是放在try裡面
        from selenium.webdriver.support.wait import WebDriverWait
        from selenium.webdriver.support import expected_conditions as EC
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(5)
        self.driver.get("https://webmail30.189.cn/w2/")  # 189郵箱
        print('start', time.ctime())

        # <iframe class="login_iframe" id="iframeLogin" allowtransparency="true" frameborder="0" scrolling="no" src="/w2/logon/UnifyLogin.do?t=1542157418806"></iframe>

        print(EC.frame_to_be_available_and_switch_to_it('iframeLogin')(self.driver))
        self.driver.find_element(By.XPATH, '//input[@name="userName" or @id="userName"]').send_keys('987')
        self.driver.switch_to.default_content()
        print('0', time.ctime())

        time.sleep(1)  # 執行速度太快

        WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it('iframeLogin'), '失敗')
        self.driver.find_element(By.XPATH, '//input[@id="userName"]').clear()
        self.driver.switch_to.default_content()
        print('00', time.ctime())

        time.sleep(1)

        print(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe[class="login_iframe"]'))(self.driver))
        self.driver.find_element(By.XPATH, '//input[@name="userName" or @id="userName"]').send_keys('654')
        self.driver.switch_to.default_content()
        print('1', time.ctime())

        time.sleep(1)

        WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe[class="login_iframe"]')), '失敗')
        self.driver.find_element(By.XPATH, '//input[@id="userName"]').clear()
        self.driver.switch_to.default_content()
        print('10', time.ctime())

        time.sleep(1)

        ABCD = self.driver.find_element(By.ID, 'iframeLogin')
        print(EC.frame_to_be_available_and_switch_to_it(ABCD)(self.driver))
        self.driver.find_element(By.XPATH, '//input[@id="userName"]').send_keys('321')
        self.driver.switch_to.default_content()
        print('2', time.ctime())

        time.sleep(1)

        ABCD1 = self.driver.find_element(By.ID, 'iframeLogin')
        WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(ABCD1), '失敗')
        self.driver.find_element(By.XPATH, '//input[@id="userName"]').clear()
        self.driver.switch_to.default_content()
        print('20', time.ctime())

        try:
            ABCD11 = self.driver.find_element(By.ID, 'iframeLogin111111')
            WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(ABCD11), '失敗')
            self.driver.find_element(By.XPATH, '//input[@id="userName"]').clear()
            self.driver.find_element(By.XPATH, '//input[@id="userName"]').send_keys('666')
            self.driver.switch_to.default_content()
        except BaseException as e11:
            print(e11)      # Message: no such element: Unable to locate element: {"method":"id","selector":"iframeLogin111111"}
        print('3', time.ctime())    # 5秒的隱式等待

        try:
            ABCD22 = (By.CSS_SELECTOR, 'iframe11111111[class="login_iframe"]')
            WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(ABCD22), '失敗')
            self.driver.find_element(By.XPATH, '//input[@id="userName"]').clear()
            self.driver.find_element(By.XPATH, '//input[@id="userName"]').send_keys('666666')
            self.driver.switch_to.default_content()
        except BaseException as e111:
            print(e111)
        print('4', time.ctime())

        try:
            WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it('22222222'), '沒能成功')
            self.driver.find_element(By.XPATH, '//input[@id="userName"]').clear()
            self.driver.find_element(By.XPATH, '//input[@id="userName"]').send_keys('6666666666')
            self.driver.switch_to.default_content()
        except BaseException as e11111:
            print(e11111)
        print('5', time.ctime())

        # WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it('22222222'), '沒能成功')
        # 報錯 selenium.common.exceptions.TimeoutException: Message: 沒能成功

        # WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe11111111[class="login_iframe"]')), '失敗')
        # 報錯 selenium.common.exceptions.TimeoutException: Message: 失敗

        # WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(self.driver.find_element(By.ID, 'iframeLogin111111')), '失敗')
        # 報錯 selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"id","selector":"iframeLogin111111"}
        print('end', time.ctime())
        time.sleep(1)
        self.driver.quit()

實際看我用例的名稱就知道寫的好幾個,但是因為此次分享在實際工作中不常見,所以就分享了一個。

交流技術 歡迎+QQ 153132336 zy
歡迎關注 微信公眾號:紫雲小站