python+selenium自动化出现复杂情况解决方法

python+selenium自动化出现复杂情况解决方法

Pythone+selenium自动化测试web项目使用的是vue框架,在编写脚本时小编出现如下错误点:

关于selenium模块出现异常:selenium.common.exceptions.StaleElementReferenceException: 的解决方案

在实现登录过程中出现了,登录之后无法操作页面的情况,用到的是滑块登录(参考网上的方法)
from selenium.common import exceptions as ex

    def __init__(self):
        options = webdriver.ChromeOptions()
        options.add_experimental_option('detach', True)
        self.driver = webdriver.Chrome(options=options)
        self.driver.get("Url")
        # 最大化窗口
        self.driver.maximize_window()
    # 测试用户登录成功
    def test_admin_login(self):
        try:
            #输入账号、密码、点击登录
            # 隐式等待,让js脚本加载完毕
            self.driver.implicitly_wait(10)
            # 切换到滑块iframe元素
            frame = self.driver.find_element(By.ID, 'tcaptcha_iframe')
            self.driver.switch_to.frame(frame)
            # 等待滑块出现时间
            WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'slideBg')))
            # 获取缺口背景图片节点
            slideBg = self.driver.find_element(By.ID, "slideBg")
            # 获取滑块图片的节点
            slideBlock = self.driver.find_element(By.ID, "slideBlock")
            sc = SlideVerificationCode(save_image=True)
            distance = sc.get_element_slide_distance(slideBlock, slideBg)
            # 滑动距离误差校正,滑动距离*图片在网页上显示的缩放比-滑块相对的初始位置
            distance = distance * (341 / 680) - 22
            print("校正后的滑动距离", distance)
            # 选择拖动滑块的节点
            slide = self.driver.find_element(By.ID, "tcaptcha_drag_button")
            # 4、进行滑动
            sc.slide_verification(self.driver, slide, distance)
            # 退出frame
            self.driver.switch_to.default_content()
            #self.driver.switch_to.window(self.driver.window_handles[0])  # 重新定位到第一个页面  
        except ex.StaleElementReferenceException:
            #刷新
            self.driver.refresh()
            
# selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
# 大概意思是元素没有连接上,具体出现异常情况,有可能跟网页刷新,等待时间等待有关
# 于是用try...except...结构来写,得导入selenium的一个exceptions模块
from selenium.common import exceptions as ex
try:
    xxxxxxxx
except ex..StaleElementReferenceException:
    xxxxxxxx (这里可以用刷新来尝试 web.refresh())
 # 补充(重要):如果加了try...except...的方法(except体里面放刷新),任然会无限刷新,

可以参考这篇文章

点击登录时滑块验证码问题

# 需要进入到滑块iframe元素中
# 切换到滑块iframe元素
frame = self.driver.find_element(By.ID, 'tcaptcha_iframe')
self.driver.switch_to.frame(frame)
# 这个过程中也会出现问题,要设置等待条件,否则也会出现错误
# 隐式等待,让js脚本加载完毕
 self.driver.implicitly_wait(10)
# 等待滑块出现时间
WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'slideBg')))

使用八大元素定位时可以用Chropath进行辅助

注意: 不要出现空格

滑块验证码操作脚本

from selenium.webdriver import ActionChains
import random, time, os
import cv2
from PIL import Image as Im
import numpy as np
import requests

class SlideVerificationCode(object):
    """滑动验证码破解"""

    def __init__(self, count=5, save_image=False):
        """
        :param count: 验证重试的次数,默认为5次
        :param save_image: 是否保存验证过程中的图片,默认不保存
        """
        self.count = count
        self.save_image = save_image

    def slide_verification(self, driver, slide_element, distance):
        """
        :param driver: driver对象
        :type driver:webdriver.Chrome
        :param slide_element: 滑块的元组
        :type slider_ele: WebElement
        :param distance:  滑动的距离
        :type: int
        :return:
        """
        # 获取滑动前页面的url地址
        start_url = driver.current_url
        print("需要滑动的距离为:", distance)
        # 根据滑动距离生成滑动轨迹
        locus = self.get_slide_locus(distance)
        print("生成的滑动轨迹为:{},轨迹的距离之和为{}".format(locus, distance))
        # 按下鼠标左键
        ActionChains(driver).click_and_hold(slide_element).perform()
        time.sleep(0.5)
        # 遍历轨迹进行滑动
        for loc in locus:
            time.sleep(0.01)
            ActionChains(driver).move_by_offset(loc, random.randint(-5, 5)).perform()
            ActionChains(driver).context_click(slide_element)
        # 释放鼠标
        ActionChains(driver).release(on_element=slide_element).perform()
        time.sleep(2)
        end_url = driver.current_url
        if start_url == end_url and self.count > 0:
            print("第{}次验证失败,开启重试".format(6 - self.count))
            self.count -= 1
            self.slide_verification(driver, slide_element, distance)

    def onload_save_img(self, url, filename="image.png"):
        """
        下载图片保存
        :param url:图片地址
        :param filename: 保存的图片名
        :return:
        """
        try:
            response = requests.get(url=url)
        except(requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError) as e:
            print("图片下载失败")
            raise e
        else:
            with open(filename, "wb") as f:
                f.write(response.content)

    def get_element_slide_distance(self, slider_ele, background_ele, correct=0):
        """
        根据传入滑块,和背景的节点,计算滑块的距离

        该方法只能计算 滑块和背景图都是一张完整图片的场景,
        如果是通过多张小图拼接起来的背景图,该方法不适用,后续会补充一个专门针对处理该场景的方法
        :param slider_ele: 滑块图片的节点
        :type slider_ele: WebElement
        :param background_ele: 背景图的节点
        :type background_ele:WebElement
        :param correct:滑块缺口截图的修正值,默认为0,调试截图是否正确的情况下才会用
        :type: int
        :return: 背景图缺口位置的X轴坐标位置(缺口图片左边界位置)
        """
        # 获取验证码的图片
        slider_url = slider_ele.get_attribute("src")
        background_url = background_ele.get_attribute("src")
        # 下载验证码背景图,滑动图片
        slider = "slider.jpg"
        background = "background.jpg"
        self.onload_save_img(slider_url, slider)
        self.onload_save_img(background_url, background)
        # 读取进行色度图片,转换为numpy中的数组类型数据,
        slider_pic = cv2.imread(slider, 0)
        background_pic = cv2.imread(background, 0)
        # 获取缺口图数组的形状 -->缺口图的宽和高
        width, height = slider_pic.shape[::-1]
        # 将处理之后的图片另存
        slider01 = "slider01.jpg"
        background_01 = "background01.jpg"
        cv2.imwrite(background_01, background_pic)
        cv2.imwrite(slider01, slider_pic)
        # 读取另存的滑块图
        slider_pic = cv2.imread(slider01)
        # 进行色彩转换
        slider_pic = cv2.cvtColor(slider_pic, cv2.COLOR_BGR2GRAY)
        # 获取色差的绝对值
        slider_pic = abs(255 - slider_pic)
        # 保存图片
        cv2.imwrite(slider01, slider_pic)
        # 读取滑块
        slider_pic = cv2.imread(slider01)
        background_pic = cv2.imread(background_01)
        # 比较两张图的重叠区域
        result = cv2.matchTemplate(slider_pic, background_pic, cv2.TM_CCOEFF_NORMED)
        # 获取图片的缺口位置
        top, left = np.unravel_index(result.argmax(), result.shape)
        # 背景图中的图片缺口坐标位置
        print("当前滑块的缺口位置:", (left, top, left + width, top + height))
        return left

    def get_image_slide_dictance(self, slider_image, background_image, correct=0):
        """
        根据传入滑块,和背景的图片,计算滑块的距离
        该方法只能计算 滑块和背景图都是一张完整图片的场景,
        如果是通过多张小图拼接起来的背景图,该方法不适用,后续会补充一个专门针对处理该场景的方法
        :param slider_iamge: 滑块图的图片
        :type slider_image: str
        :param background_image: 背景图的图片
        :type background_image: str
        :param correct:滑块缺口截图的修正值,默认为0,调试截图是否正确的情况下才会用
        :type: int
        :return: 背景图缺口位置的X轴坐标位置(缺口图片左边界位置)
        """
        slider_pic = cv2.imread(slider_image, 0)
        background_pic = cv2.imread(background_image, 0)
        width, height = slider_pic.shape[::-1]
        slider01 = "slider01.jpg"
        background_01 = "background01.jpg"
        cv2.imwrite(background_01, background_pic)
        cv2.imwrite(slider01, slider_pic)
        slider_pic = cv2.imread(slider01)
        slider_pic = cv2.cvtColor(slider_pic, cv2.COLOR_BGR2GRAY)
        slider_pic = abs(255 - slider_pic)
        cv2.imwrite(slider01, slider_pic)
        slider_pic = cv2.imread(slider01)
        background_pic = cv2.imread(background_01)
        result = cv2.matchTemplate(slider_pic, background_pic, cv2.TM_CCOEFF_NORMED)
        top, left = np.unravel_index(result.argmax(), result.shape)
        print("当前滑块的缺口位置:", (left, top, left + width, top + height))
        if self.save_image:
            loc = (left + correct, top + correct, left + width - correct, top + height - correct)
            self.image_crop(background_image, loc)
        else:
            os.remove(slider01)
            os.remove(background_01)
        return left

    @classmethod
    def get_slide_locus(self, distance):
        """
        根据移动坐标位置构造移动轨迹,前期移动慢,中期块,后期慢
        :param distance:移动距离
        :type:int
        :return:移动轨迹
        :rtype:list
        """
        remaining_dist = distance
        locus = []
        while remaining_dist > 0:
            ratio = remaining_dist / distance
            if ratio < 0.2:
                span = random.randint(2, 8)
            elif ratio > 0.8:
                span = random.randint(5, 8)
            elif ratio > 1:
                span = random.randint(10, 16)
            else:
                span = random.randint(15, 24)
            locus.append(span)
            remaining_dist -= span
        return locus

    def image_crop(self, image, location, new_name="new_image.png"):
        """
        对图片的指定位置进行截图
        :param image: 被截取图片的坐标位置
        :param location:需要截图的坐标位置:(left,top,right,button)
        :type location: tuple
        :return:
        """
        image = Im.open(image)
        imagecrop = image.crop(location)
        imagecrop.save(new_name)
登录脚本中引入SlideVerificationCode
import time
from tkinter.tix import Select
from selenium.common import exceptions as ex
from selenium.common.exceptions import StaleElementReferenceException
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common.test_code import SlideVerificationCode

class TestAdminLoginOk(object):
    def __init__(self):
        self.driver = webdriver.Chrome()
        # 指定我们需要的页面
        self.driver.get("https://qzone.qq.com/")
        # 最大化窗口
        self.driver.maximize_window()

    # 测试用户登录成功
    def test_admin_login(self):
        expected="xxxxx"
        try:
            # 输入账号、密码、点击登录
            # 隐式等待,让js脚本加载完毕
            self.driver.implicitly_wait(10)
            # 切换到滑块iframe元素
            frame = self.driver.find_element(By.ID, 'tcaptcha_iframe')
            self.driver.switch_to.frame(frame)
            # 等待滑块出现时间
            WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'slideBg')))
            # 获取缺口背景图片节点
            slideBg = self.driver.find_element(By.ID, "slideBg")
            # 获取滑块图片的节点
            slideBlock = self.driver.find_element(By.ID, "slideBlock")
            sc = SlideVerificationCode(save_image=True)
            distance = sc.get_element_slide_distance(slideBlock, slideBg)
            # 滑动距离误差校正,滑动距离*图片在网页上显示的缩放比-滑块相对的初始位置
            distance = distance * (341 / 680) - 22
            print("校正后的滑动距离", distance)
            # 选择拖动滑块的节点
            slide = self.driver.find_element(By.ID, "tcaptcha_drag_button")
            # 4、进行滑动
            sc.slide_verification(self.driver, slide, distance)
            # 退出frame
            self.driver.switch_to.default_content()
            # 验证登录之后的页面title和我们想要的title一致,一致表示登录成功
            assert self.driver.title == expected
            
        except ex.StaleElementReferenceException:

            self.driver.refresh()
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值