吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 710|回复: 9
上一主题 下一主题
收起左侧

[Python 原创] 结合DrissionPage、OCR、OpenCV,实现WEB中canvas元素自动化

  [复制链接]
跳转到指定楼层
楼主
Lyss07 发表于 2024-11-5 09:40 回帖奖励
本帖最后由 Lyss07 于 2024-11-5 09:43 编辑

  因为工作中遇到类似的需求(需要对某C/S架构改造为B/S架构的某系统,进行自动化测试及相应定制的自动化框架改造、重构),其中,该系统将以前的C/S架构,gui画的界面直接平移到Web框架中,运用了canvas画布元素,从而减少了代码的改造和重构。因此,遇到了需要在画布元素中进行自动化测试的需求。
  简言之,一个画布元素中包含了各种按钮、下拉框等等操作,但通过DrissionPage(或者类selenium)的库,识别的时候,获取不到画布中的按钮等元素的定位,从而在实现自动化时无法通过直接相关元素信息直接操作。
  一般地,两个思路,一个是进行抓包,获取操作canvas时发送的数据及相关操作,模拟进行,但由于(<del>我太菜了</del>)某系统涉及分工比较细,而且人员分布较为广,类似的尝试不太好启动;
第二个思路是,通过截图或者识别canvas画布当前的图像,利用OpenCV进行图片模板匹配,从而实现自动化。故,我综合内部的一些思路一点个人想法,学习和利用DrissionPage中的思路,将canvas元素视为一个特殊的页面元素,实现了一个第二个思路的解决方案。
  实现的代码:(gitee上:https://gitee.com/lz-lyss/canvas-element-automation) ,这里只简单写下主要的代码,其他部分可以看下我代码库的链接
[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-

import os
from typing import NoReturn, Any

from DrissionPage._units.waiter import ElementWaiter
from DrissionPage.items import ChromiumElement
from DrissionPage.errors import ElementNotFoundError
# from DrissionPage import WebPage

from src import CANVAS_BASE_SRC_PATH
from src._typing import MatchLocation
from src._base import logger
from src._items.location_clicker import LocationClicker

__all__ = ('is_canvas', 'CanvasElement')


class CanvasElement(ChromiumElement):

    # 原始画布元素
    _src_canvas: ChromiumElement | None = None
    # 原始画布元素在页面中的位置
    _src_location: MatchLocation | None = None

    def __init__(self, chrome_ele: ChromiumElement):
        """
        画布元素
        Args:
            chrome_ele(ChromiumElement): 原始画布元素,从上述操作中传入的ChromiumElement对象
        """
        super().__init__(chrome_ele.owner, node_id=chrome_ele._node_id)
        if not is_canvas(self):
            raise TypeError(f'当前元素({self.tag}) 不是Canvas元素,无法使用CanvasElement.')
        # 页面属性
        self.tab = self.owner.tab
        # 类型
        self._type = 'CanvasElement'
        # 更新原始画布元素属性
        self._src_canvas: ChromiumElement | None = chrome_ele
        # 更新滚动元素属性
        self._scroll = self._src_canvas.scroll
        # 更新原始画布元素在页面中的位置
        # [self._src_canvas.rect.location[0], self._src_canvas.rect.location[1]]
        self._src_location: MatchLocation | None = self._src_canvas.rect.location
        # 原始画布元素保存到的截图路径
        self._src_image_path: str | None = None
        # 元素操作动作
        self._clicker: LocationClicker | None = None
        # 元素等待动作
        self._wait: ElementWaiter | None = None
        # 元素查找文字动作
        self._find_text = None

    @property
    def click(self) -> LocationClicker:
        """
        返回用于点击的对象
        Returns:

        """
        if self._clicker is None:
            self._clicker = LocationClicker(self)
        return self._clicker

    @property
    def wait(self):
        """
        返回用于等待的对象
        Returns:

        """
        if self._wait is None:
            self._wait = ElementWaiter(self.owner, self)
        return self._wait

    @property
    def scroll(self):
        return self._scroll

    @property
    def src_image_path(self) -> str | None:
        """
        返回原始画布元素保存到的截图路径
        Returns:
            str|None: 截图路径
        """
        return self._src_image_path

    @staticmethod
    def __default_image_name(i_name: str):
        """
        设置默认图片名称,默认保存成高清的jpeg文件
        Args:
            i_name(str): 图片名称

        Returns:
            res_name(str): 图片名称

        """
        res_name = os.path.splitext(i_name)[0]
        file_extension = os.path.splitext(i_name)[1]
        match file_extension:
            case '.jpg' | '.jpeg' | '.png' | '.webp':
                res_name = i_name
            case _:
                res_name = f'{res_name}.jpeg'
        return res_name

    # 暂不启用二次初始化方法,不符合类的设计原则
    # def __update_init(self,  page: WebPage | None = None, ele_str: str | None = None) -> NoReturn:
    #     """
    #     更新初始化, 用于更新原始画布元素
    #     Returns:
    #
    #     """
    #     if not page:
    #         page = self.owner.tab.page
    #     # 更新原始画布元素
    #     if ele_str:
    #         tar_ele = page.ele(ele_str)
    #     else:
    #         tar_ele = page.ele('x://canvas')
    #     self.__init__(tar_ele)

    def __update_src_canvas_picture(self, name: str | None = None):
        if not name:
            import time
            time_str = time.strftime('%Y%m%d%H%M%S', time.localtime())
            name = f'basic_canvas_{time_str}'
        self.save_to_picture(name=name)

    def save_to_picture(self, name: str | None = None,
                        path: str = CANVAS_BASE_SRC_PATH,
                        as_bytes=None,
                        as_base64=None,
                        scroll_to_center=True) -> bytes | str:
        """
        将元素截图,并保存到本地
        Args:
            path(str): 保存路径
            name(str): 完整文件名,后缀可选 'jpg','jpeg','png','webp', 不传时默认保存成更高清的jpeg文件
            as_bytes(bool|str): 是否以字节形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数和as_base64参数无效
            as_base64(bool|str): 是否以base64字符串形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数无效
            scroll_to_center(bool): 截图前是否滚动到视口中央

        Returns:
            ret_path(bytes|str): 图片完整路径或字节文本

        """
        if scroll_to_center:
            self.scroll.to_see(center=True)

        left, top = self.rect.location
        width, height = self.rect.size
        left_top = (left, top)
        right_bottom = (left + width, top + height)
        if not name:
            name = f'{self.tag}.jpeg'
        else:
            # 处理下文件名,如果没传文件名后缀,默认保存成更高清的jpeg文件
            name = self.__default_image_name(name)

        # 更新截图路径
        self._src_image_path = os.path.abspath(os.path.join(path, name))

        return self.owner._get_screenshot(path, name, as_bytes=as_bytes, as_base64=as_base64, full_page=False,
                                          left_top=left_top, right_bottom=right_bottom, ele=self)

    def __splice_position(self, target_location: MatchLocation) -> MatchLocation:
        """
        获取拼接位置
        Args:
            target_location(MatchLocation): 目标位置

        Returns:
            res_location(MatchLocation): 拼接位置
        """
        # 默认值取当前默认画布元素的位置
        res_location = self._src_location
        if target_location and len(target_location) == 2:
            res_location = (res_location[0] + target_location[0], res_location[1] + target_location[1])
        else:
            raise ValueError(f'参数错误! 当前像素位置为: {target_location} ,不符合操作动作要求!')

        return res_location

    @staticmethod
    def __get_one_match(match_list: list[MatchLocation], target_number: int | None = 0) -> MatchLocation | None:
        """
        多个位置信息中,获取其中一个指定匹配位置
        Args:
            match_list(list): 匹配位置列表
            target_number(int|None): 目标匹配位置索引,默认为0

        Returns:
            res_location(MatchLocation|None): 匹配位置

        """
        if match_list:
            m_len = len(match_list)
            if target_number is None or target_number >= m_len:
                logger.warning(f'异常的获取索引:{target_number},将默认取第一个位置信息!')
                target_number = 0
            res_location = match_list[target_number]
        else:
            res_location = None
        return res_location

    # <editor-fold desc="图片模板匹配相关辅助方法">
    def __init_template_match(self):
        """"""
        from src._match import TemplateMatch
        p_match = TemplateMatch(self._src_image_path)
        return p_match

    def _get_location_match(self, template_path: str) -> MatchLocation | None:
        """
        根据模板图片,获取匹配位置
        一个模板,一个最优匹配位置,结果是单个匹配位置
        Args:
            template_path(str): 模板图片路径

        Returns:
            res_location(list|None): 匹配位置,如果没有匹配到合理结果,为None

        """
        t_match = self.__init_template_match()
        match_location = t_match.get_template_location(template_path)
        return match_location

    # </editor-fold>  图片模板匹配相关辅助方法

    # <editor-fold desc="文字匹配相关辅助方法">
    def __init_text_match(self, ocr_engine: str = 'paddleocr', model_path: str | None = None,
                          update_special_symbol: str | None = None):
        """
        初始化文字匹配对象
        Args:
            ocr_engine(str): ocr引擎,默认为paddleocr
            model_path(str|None): 模型路径,默认为None,取当前引擎默认的模型路径
            update_special_symbol(str|None): 更新正则替换的特殊字符,默认为None,取默认的正则替换字符

        Returns:
            t_match(TextMatch): 文字匹配对象

        """
        from src._match import TextMatch
        t_match = TextMatch(image_path=self._src_image_path, engine=ocr_engine, model_path=model_path)
        # 更新正则替换的特殊字符
        if update_special_symbol is not None:
            t_match.update_special_symbol(special_symbol=update_special_symbol)
        return t_match

    # </editor-fold>  文字匹配相关辅助方法

    # <editor-fold desc="图片模板匹配相关操作方法">
    def click_left(self, template_image_path: str,
                   is_update_src_picture: bool = False,
                   update_picture_name: str | None = None) -> NoReturn:
        """
        左键单击
        单个模板,一个最优匹配位置
        Args:
            template_image_path(str): 模板图片路径
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        location = self._get_location_match(template_path=template_image_path)
        if location:
            res_location = self.__splice_position(location)
            self.click.left(res_location)

    def click_right(self, template_image_path: str,
                    is_update_src_picture: bool = False,
                    update_picture_name: str | None = None
                    ) -> NoReturn:
        """
        右键单击
        单个模板,一个最优匹配位置
        Args:
            template_image_path(str): 模板图片路径
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        location = self._get_location_match(template_path=template_image_path)
        if location:
            res_location = self.__splice_position(location)
            self.click.right(res_location)

    def click_middle(self, template_image_path: str,
                     is_update_src_picture: bool = False,
                     update_picture_name: str | None = None) -> NoReturn:
        """
        中键单击(注:其他操作,如滚动等为页面操作,与其他ChromeElement操作一致)
        单个模板,一个最优匹配位置
        Args:
            template_image_path(str): 模板图片路径
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        # canvas_ele_str(str|None): 定位canvas元素的字符串,用于更新截图
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        location = self._get_location_match(template_path=template_image_path)
        if location:
            res_location = self.__splice_position(location)
            self.click.middle(res_location)

    def click_with_offset(self, template_image_path: str, offset_x: float = 0, offset_y: float = 0,
                          button: str = 'left',
                          is_update_src_picture: bool = False,
                          update_picture_name: str | None = None) -> NoReturn:
        """
        单次点击,并设置偏移量
        单个模板,一个最优匹配位置
        Args:
            template_image_path(str): 模板图片路径
            offset_x(float): x轴偏移量,横向偏移量
            offset_y(float): y轴偏移量,纵向偏移量
            button(str): 鼠标按键,默认为左键
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        location = self._get_location_match(template_path=template_image_path)
        if location:
            res_location = self.__splice_position(location)
            self.click.at(res_location, offset_x=offset_x, offset_y=offset_y, button=button)

    def multiple_click(self, template_image_path: str, times: int = 2,
                       is_update_src_picture: bool = False,
                       update_picture_name: str | None = None) -> NoReturn:
        """
        多次点击(注:默认多次点击时仅允许左键)
        单个模板,一个最优匹配位置
        Args:
            template_image_path(str): 模板图片路径
            times(int): 点击次数,默认为2次
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        location = self._get_location_match(template_path=template_image_path)
        if location:
            res_location = self.__splice_position(location)
            self.click.multi(res_location, times=times)

    def multiple_click_with_offset(self, template_image_path: str, offset_x: float = 0, offset_y: float = 0,
                                   times: int = 2,
                                   is_update_src_picture: bool = False,
                                   update_picture_name: str | None = None) -> NoReturn:
        """
        多次点击,并设置偏移量(注:默认多次点击时仅允许左键)
        单个模板,一个最优匹配位置
        Args:
            template_image_path(str): 模板图片路径
            offset_x(float): x轴偏移量,横向偏移量
            offset_y(float): y轴偏移量,纵向偏移量
            times(int): 点击次数,默认为2次
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        location = self._get_location_match(template_path=template_image_path)
        if location:
            res_location = self.__splice_position(location)
            self.click.at(res_location, count=times, offset_x=offset_x, offset_y=offset_y)

    def move_to(self, template_image_path: str, offset_x: float = 0, offset_y: float = 0,
                is_update_src_picture: bool = False,
                update_picture_name: str | None = None) -> NoReturn:
        """
        移动鼠标到指定位置
        Args:
            template_image_path(str): 模板图片路径
            offset_x(float): x轴偏移量,横向偏移量
            offset_y(float): y轴偏移量,纵向偏移量
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:

        """
        try:
            if is_update_src_picture:
                # self.__update_init(ele_str=canvas_ele_str)
                self.__update_src_canvas_picture(name=update_picture_name)
            location = self._get_location_match(template_image_path)
            if location:
                res_location = self.__splice_position(location)
                location = (res_location[0], res_location[1])
                self.owner.actions.move_to(location, offset_x=offset_x, offset_y=offset_y)
        except Exception as err:
            logger.error(f'移动鼠标到指定位置失败,{err.__repr__()}')

    def is_exists(self, template_image_path: str,
                  is_update_src_picture: bool = False,
                  update_picture_name: str | None = None) -> bool:
        """
        根据模板图片,判断元素是否存在
        Args:
            template_image_path(str | list): 模板图片路径,单个或多个
                多个时,根据模板文件数量与匹配结果数量进行判断,若匹配结果数量等于模板文件数量,则认为元素存在
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:
            res_exists(bool): 是否存在

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        t_match = self.__init_template_match()
        res_exists = t_match.is_match(template=template_image_path)
        return res_exists

    def is_all_exists(self, template_image_path: str | list,
                      counts: int = 2,
                      is_update_src_picture: bool = False,
                      update_picture_name: str | None = None) -> bool:
        """
        根据模板图片,判断元素是否存在
        单个模板,多个匹配位置 或 多模板
        Args:
            template_image_path(str): 模板图片路径
            counts(int): 匹配结果数量,单模板多目标匹配时可填该值,多模板时不要填!默认为2
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称

        Returns:
            bool: 是否存在

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        t_match = self.__init_template_match()
        res_exists = t_match.is_all_match(templates=template_image_path, match_count=counts)
        return res_exists

    # </editor-fold>  图片模板匹配相关操作方法

    def canvas_input(self, input_val: Any, ele_info: str = None, is_clear: bool = True) -> NoReturn:
        """
        输入文本
        Args:
            input_val(Any): 输入值
            ele_info(str): 辅助定位输入框元素的元素信息,默认缺省时将搜寻canvas元素的兄弟元素中的第一个input元素
            is_clear(bool): 是否清空输入框,默认为True

        Returns:
            NoReturn

        """
        if ele_info:
            tar_input = self.owner.ele(ele_info)
        else:
            tar_input = self.ele('x:../input')
        if not tar_input:
            raise ElementNotFoundError(f'当前页面({self.owner.url})未找到可进行输入的输入框元素!')
        # 清空输入框
        if is_clear:
            # 如果需要清空输入框,则先清空
            tar_input.clear()

        # 输入值
        tar_input.owner.actions.type(input_val)

    # <editor-fold desc="文字匹配相关操作方法">
    # 对当前canvas保存的图片进行OCR识别后,定位到文本位置
    # is_filter_special_symbol 针对的是OCR识别到的文字中的特殊字符是否进行过滤,而不是 指定的目标匹配文本中的特殊字符
    def is_text_exists(self, text: str, is_filter_special_symbol: bool = True,
                       is_update_src_picture: bool = False,
                       update_picture_name: str | None = None,
                       **kwargs) -> bool:
        """
        判断指定文本是否存在
        Args:
            text(str): 文本
            is_filter_special_symbol(bool): 是否过滤特殊字符
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称
            kwargs(dict): 其他参数。用于初始化TextMatch时的配置,详情见__init_text_match方法及TextMatch类

        Returns:
            res_match(bool): 是否存在

        """
        # 是否更新当前canvas元素的截图
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        # 初始化TextMatch
        t_match = self.__init_text_match(**kwargs)
        # 获取文字匹配判断结果
        res_match = t_match.text_is_exists(text=text, is_filter_special_symbol=is_filter_special_symbol)
        return res_match

    def is_all_text_exists(self, texts: list[str] | set[str] | tuple[str],
                           is_filter_special_symbol: bool = True,
                           is_update_src_picture: bool = False,
                           update_picture_name: str | None = None,
                           **kwargs) -> bool:
        """
        判断所有指定文本是否存在
        Args:
            texts(list): 文本列表
            is_filter_special_symbol(bool): 是否过滤特殊字符
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称
            kwargs(dict): 其他参数。用于初始化TextMatch时的配置,详情见__init_text_match方法及TextMatch类

        Returns:
            bool: 是否存在

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        # 初始化TextMatch
        t_match = self.__init_text_match(**kwargs)
        # 获取文字匹配判断结果
        res_match = t_match.texts_is_all_exists(text=texts, is_filter_special_symbol=is_filter_special_symbol)
        return res_match

    def click_text(self,
                   text: str,
                   is_filter_special_symbol: bool = True,
                   is_update_src_picture: bool = False,
                   update_picture_name: str | None = None,
                   **kwargs) -> NoReturn:
        """
        点击指定文本
        Args:
            text(str): 文本
            is_filter_special_symbol(bool): 是否过滤特殊字符
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称
            **kwargs(dict): 其他参数。用于初始化TextMatch时的配置,详情见__init_text_match方法及TextMatch类

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        # 初始化TextMatch
        t_match = self.__init_text_match(**kwargs)
        # 获取匹配结果
        match_location = t_match.text_get_match_location(text=text, is_filter_special_symbol=is_filter_special_symbol)
        # 匹配到了
        if match_location:
            # 拼接匹配位置
            location = self.__splice_position(match_location)
            self.click.left(location)

    def multiple_click_text(self,
                            text: str,
                            times: int = 2,
                            is_filter_special_symbol: bool = True,
                            is_update_src_picture: bool = False,
                            update_picture_name: str | None = None,
                            **kwargs
                            ) -> NoReturn:
        """
        点击指定文本,多次
        Args:
            text(str): 文本
            times(int): 点击次数
            is_filter_special_symbol(bool): 是否过滤特殊字符
            is_update_src_picture(bool): 是否更新源图片。即,当前canvas元素发生变化时,需要更新原始默认截图
            update_picture_name(str|None): 更新截图的名称
            **kwargs(dict): 其他参数。用于初始化TextMatch时的配置,详情见__init_text_match方法及TextMatch类

        Returns:

        """
        if is_update_src_picture:
            # self.__update_init(ele_str=canvas_ele_str)
            self.__update_src_canvas_picture(name=update_picture_name)
        # 初始化TextMatch
        t_match = self.__init_text_match(**kwargs)
        # 获取匹配结果
        match_location = t_match.text_get_match_location(text=text, is_filter_special_symbol=is_filter_special_symbol)
        # 匹配到了
        if match_location:
            # 拼接匹配位置
            location = self.__splice_position(match_location)
            self.click.multi(location, times=times)

    # </editor-fold> 文字匹配相关操作方法


def is_canvas(ele: ChromiumElement) -> bool:
    ele_tag = ele.tag
    if 'canvas' in ele_tag:
        return True
    else:
        return False


if __name__ == '__main__':

    from DrissionPage import WebPage

    page = WebPage(mode='d')
    # https://www.bilibili.com/read/cv15814078/
    # https://www.bilibili.com/
    page.get('https://www.bilibili.com/read/cv15814078/')
    # [@class="ring-progress"]
    canvas = page.ele('x://canvas[@class="ring-progress"]')
    # ("2d")
    c_ele = CanvasElement(canvas)
    c_ele.save_to_picture(name='test_canvas')
    # c_ele.move_to(template_image_path='./coin_test.jpeg')
    c_ele.click_right(template_image_path='test_pictures/coin_test.jpeg')
    # context = canvas.get_screenshot('./test_canvas', 'test_canvas.png')
    # c_ele.input('123')


  因为这个解决思路我不太确定是不是最好的,但是之前内部测试是可以完成自动化,而且也稍微准确一点点,所以发出来让各位大佬给看下这个思路(请忽略代码水平的问题,谢谢),请多多指教,感谢!

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

11#
hantiany 发表于 2024-11-15 10:42
快进收藏夹吃灰吧
10#
 楼主| Lyss07 发表于 2024-11-6 11:46 |楼主
douchanshenghuo 发表于 2024-11-5 22:03
之前看过一个方法,是利用selenium的js注入方式,获取所要操作的元素坐标,再使用 ActionChains对应的鼠标 ...

是的是的,我之前就是这个思路,
问题是我们内部那个某系统的很多功能都在canvas画布上,占页面的70%以上了,所以才取用的模板匹配。
画布元素是可以获取到的,但是里面的按钮、输入框啥的,就没法获取到了。js的话就需要去跟平台的开发沟通,出于不太方便的原因,最后就放弃了这个方向。
  然后DrissionPage是有类似的动作操作的,我核心的改动就是将canvas元素继承它(DP)的page类,将canvas元素视为一个特殊的元素(page),然后通过OpenCV模板匹配获取到的相对位置,作为坐标点,传递给DP动作类,实现操作。底层逻辑也主要是获取坐标位置
9#
 楼主| Lyss07 发表于 2024-11-6 11:31 |楼主
TrevorP 发表于 2024-11-5 23:32
这么玩可行是可行,属实太累了,看应用的复杂程度是不是值得了
正好前段时间玩DNF,有个和 lz 类似的思路
...

是的,用于自动化测试还行,应用场景如果是复杂的变化还是不合适的,
您这个我学习下,好东西,收藏了
8#
TrevorP 发表于 2024-11-5 23:32
这么玩可行是可行,属实太累了,看应用的复杂程度是不是值得了
正好前段时间玩DNF,有个和 lz 类似的思路
https://github.com/yosaa/dnfm-auto
7#
douchanshenghuo 发表于 2024-11-5 22:03
之前看过一个方法,是利用selenium的js注入方式,获取所要操作的元素坐标,再使用 ActionChains对应的鼠标动作去执行对应的操作。你这个也可以参考,谢谢分享。
6#
xinxiu 发表于 2024-11-5 21:26
收藏等于学会
5#
大兵马元帅 发表于 2024-11-5 19:03
TrevorP 发表于 2024-11-5 11:58
pywinctl,这个满足吗

谢谢 我去试试,
4#
Godjk 发表于 2024-11-5 16:12
思路打开,学习一下
3#
TrevorP 发表于 2024-11-5 11:58
大兵马元帅 发表于 2024-11-5 11:23
借楼:python有没有可以后台操作键盘鼠标的库,最好也能带后台找图功能,谢谢

pywinctl,这个满足吗
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 09:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表