【Python】selenium实现滚动条滑动效果

封装自动化方法:selenuimtools.py

from selenium.common import TimeoutException, InvalidArgumentException, JavascriptException
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
import tools.log as Log
from tools.log import MyException
from time import sleep
# selenuim 自动化方法脚本
# 封装组件相关操作
class ElementOperate:
    def __init__(self, driver, find_timeout: float = 5, sleep_time: float = 1.5):
        self.driver = driver
        # 等待组件最长时间
        self.find_timeout = find_timeout
        # 操作之间的间隔时间
        self.sleepT = sleep_time

    # 获取组件
    def find_element(self, find_type, element: str) -> WebElement:
        el = None
        by = None
        driver = self.driver
        # 类型转换
        if find_type == "id":
            by = By.ID
        if find_type == "xpath":
            by = By.XPATH
        elif find_type == "link":
            by = By.LINK_TEXT
        elif find_type == "partial_link":
            by = By.PARTIAL_LINK_TEXT
        elif find_type == "name":
            by = By.NAME
        elif find_type == "tag":
            by = By.TAG_NAME
        elif find_type == "class":
            by = By.CLASS_NAME
        elif find_type == "css":
            by = By.CSS_SELECTOR

        
        try:
            if by is None:
            	# 如果用户未按要求输入,则停止查询
            	raise MyException(f"查找类型错误,类型【{find_type}】不符合要求!!!")
                
            # 显式等待组件出现,
            el = WebDriverWait(driver=driver, timeout=self.find_timeout).until(
                lambda dv: dv.find_element(by, element), f"查找组件【{element}】超时,组件未在指定时间内出现"
            )
        except TimeoutException as e:
            Log.error(e.msg)
        except MyException as e:
            Log.error(e)
        finally:
            return el

    # 点击组件
    def click(self, by: str, element: str) -> bool:
        el_button = self.find_element(by, element)
        # 判断是否找到组件
        if el_button:
            el_button.click()
            return True
        return False

    # 输入框输入
    def send_keys(self, by: str, element: str, context: str) -> bool:
        el_input = self.find_element(by, element)
        # 判断是否找到组件
        if el_input:
            # 先清除输入框内容
            el_input.clear()
            el_input.send_keys(context)
            return True
        return False

    # 页面滚动
    # scroll_y: 滑动高度百分比,不选则滑动置底
    # times: 完成滑动所需时间,不选则立即完成滑动
    # sleep_time: 多少秒后开始执行
    # is_await:是否等待滑动结束
    def scrollTo(self, scroll_y: float = None,
                 times: int = 0,
                 sleep_time: float = None,
                 is_await: bool = True) -> bool:
        # 等待页面加载完成,如果页面未加载完就执行,会导致scrollHeight属性错误,变成上一页面的高度而不是当前页面的高度
        sleep(self.sleepT if sleep_time is None else sleep_time)
        # 时间为负数时设置为0
        times = 0 if times < 0 else (times * 1000)

        # 滚动条高度
        if scroll_y is None:
            # 获取页面内容高度
            scroll_y = self.execute_script("return document.body.scrollHeight")

        # 滚动条滑动脚本
        js_code = '''
                        const ScrollTop = (number = 0, time) => {
                            if (!time) {
                                document.body.scrollTop = document.documentElement.scrollTop = number;
                                return number;
                            }
                            const spacingTime = 20; 
                            let spacingInex = time / spacingTime;
                            let nowTop = document.body.scrollTop + document.documentElement.scrollTop;
                            let everTop = (number - nowTop) / spacingInex; 
                            let scrollTimer = setInterval(() => {
                                if (spacingInex > 0) {
                                    spacingInex--;
                                    ScrollTop(nowTop += everTop);
                                } else {
                                    clearInterval(scrollTimer); 
                                }
                            }, spacingTime);
                        };
                    '''
        js_code = js_code + f"ScrollTop({scroll_y}, {times});"
        self.execute_script(js_code=js_code)
        position = 0
        # 判断滚动条是否已停止滚动
        while is_await:
            # 每隔0.1s检测一次
            sleep(0.1)
            # 获取当前位置
            new_position = self.execute_script(
                'return document.body.scrollTop + document.documentElement.scrollTop;')
            if new_position == position:
                is_await = False
            position = new_position
        return True

    async def async_scrollTo(self):
        pass

    # js 脚本执行,默认同步
    def execute_script(self, js_code: str, is_async: bool = False):
        if js_code is None or js_code == "":
            raise MyException("js脚本为空")
        try:
            if not is_async:
                # 同步执行:适合执行时间较短的js。webdriver 会等待执行结果,然后继续执行后续代码
                return self.driver.execute_script(js_code)
            else:
                # 异步执行:通常执行时间比较长的js。webdriver 不需要等待执行结果,直接执行后续代码
                return self.driver.execute_async_script(js_code)
        except (InvalidArgumentException, JavascriptException):
            Log.error(f"js脚本异常,无法被执行,当前执行脚本内容如下:\n {js_code}")
        except MyException as e:
            Log.error(e)

自定义工具类:tools.py

# 输出文本修饰
def error(msgs):
    print(f"\033[31m****异常报错:{msgs}\033[0m")


def info(msgs):
    print(f"\033[32m****信息输出:{msgs}\033[0m")


def warn(msgs):
    print(f"\033[33m****警告信息:{msgs}\033[0m")


# 自定义异常类 MyException,配合log使用
class MyException(Exception):  # 继承异常类
    def __init__(self, msg):  # 重写父类的__init__方法
        self.msg = msg

测试代码

from selenium import webdriver
from tools.selenuimtools import ElementOperate

class AutoWebTest:
    def __init__(self):
        # 设置浏览器不自动关闭
        options = webdriver.ChromeOptions()
        options.add_experimental_option('detach', True)
        self.driver = webdriver.Chrome(options=options)
        # 设置浏览器全屏
        self.driver.maximize_window()

    # 打开网站
    def open_web(self, request):
        self.driver.get(request)
        driver = ElementOperate(self.driver)
        driver.send_keys('id', "kw", "selenium")
        driver.click('id', 'su')

        driver.scrollTo(times=10,is_await=False)
        # 当设置is_await为FALSE时,会看到浏览器还在滑动滚动条,但程序已向下执行
        # 而为TRUE或者不设置时,会看到程序等待浏览器滑动滚动条完毕后,才会向下执行
        print("已执行")
        
if __name__ == "__main__":
    str = "https://www.baidu.com"

    AutoWebTest().open_web(str)

js滑动脚本解析

const ScrollTop = (number = 0, time) => {
                            if (!time) {
                                document.body.scrollTop = document.documentElement.scrollTop = number;
                                return number;
                            }
                            const spacingTime = 20; // 设置循环的间隔时间  值越小消耗性能越高
                            let spacingInex = time / spacingTime; // 计算循环的次数
                            let nowTop = document.body.scrollTop + document.documentElement.scrollTop; // 获取当前滚动条位置
                            let everTop = (number - nowTop) / spacingInex; // 计算每次滑动的距离
                            let scrollTimer = setInterval(() => {
                                if (spacingInex > 0) {
                                    spacingInex--;
                                    ScrollTop(nowTop += everTop);
                                } else {
                                    clearInterval(scrollTimer); // 清除计时器
                                }
                            }, spacingTime);
                        };

滚动条滑动主要实现在scrollTo方法中,其他方法只是对selenium操作方法的二次封装;

脚本执行调用execute_script方法,值得注意的是,虽然execute_script是同步执行脚本,但执行滑动js脚本,触发定时器方法后,走完后续代码就会返回结果,而不会等定时器(滑动效果)结束;
如果希望方法能等待滑动结束,可通过循环执行js脚本赋值document.body.scrollTop或其他方式实现,也可以像我一样,在后面写个循环不停去判断当前滑动条位置,以此判断滑动动画是否已结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值