纯影刀实现xpath跨域
Agent驿站
编辑于 2023年06月29日 22:39

。。。。

代码块
Python
自动换行
复制代码
# 使用提醒:
# 1. xbot包提供软件自动化、数据表格、Excel、日志、AI等功能
# 2. package包提供访问当前应用数据的功能,如获取元素、访问全局变量、获取资源文件等功能
# 3. 当此模块作为流程独立运行时执行main函数
# 4. 可视化流程中可以通过"调用模块"的指令使用此模块

import xbot
from xbot import print, sleep
from . import package



from .package import _selector_store, selector, resources
from .package import variables as glv

from xbot.selector import SelectorStore, ImageSelectorStore
from xbot.primitives import VariableDict, ResourceReader, _sdmodules
import xml.etree.ElementTree as ET

import os, re
import time

def generate_selector(uids, last_name=None, last_uid=None):
    value = {"name":"iframe", "type":"simple"}
    path = []
    for uid in uids:
        path.append({"name":"iframe","type":"web","attributes": [{'name': 'diy-uid', 'value': f"{uid}", 'required': True, 'operator': 'Equal'}]})
    if last_name:
        path.append({"name":last_name,"type":"web","attributes": [{'name': 'diy-uid', 'value': f"{last_uid}", 'required': last_uid is not None, 'operator': 'Equal'}]})
    value["path"] = path
    return xbot.selector.Selector(value)  



def get_uid(uids=[]):
    uid = str(int(time.time() * 1000))
    if uid in uids:
        uid = str(int(time.time() * 1000))  
    return uid    


class IframePage:
    def __init__(self, web_page, iframe=None, uids=[], iframe_page_list=[]):
        self.web_page = web_page
        self.iframe = web_page if iframe is None else iframe
        self.iframe_page_list = iframe_page_list
        self.uids = uids


    def read_ele(self, uids, last_name, last_uid=None, single=True):
        _selector = generate_selector(uids, last_name=last_name, last_uid=last_uid)
        if single:
            return self.web_page.find(_selector, timeout=0)
        return self.web_page.find_all(_selector, timeout=0)


    def to_iframe(self, xpath, find_descendant_iframe=False):
        """
        通过xpath切换到iframe
        """
        
        if find_descendant_iframe:
            total = 0
            list_iframe_page = self.find_all_iframe()
            new_iframe_page = None
            for iframe_page in list_iframe_page:
                try:
                    new_iframe_page = iframe_page.to_iframe(xpath)
                    if total != 0:
                        raise Exception("找到多个iframe元素,无法唯一定位")
                    total += 1
                except Exception as e:
                    msg = e.args[0]
                    if msg == "找到多个元素,无法唯一定位" or msg == "找到多个iframe元素,无法唯一定位":
                        raise
            
            if new_iframe_page is None:
                raise Exception("未找到元素")
            
            return new_iframe_page

        iframe_ele = self.find_ele(xpath, True)
        uids = self.uids.copy()
        return IframePage(self.web_page, iframe_ele, uids)

    # @record_time
    def find_ele(self, xpath, is_iframe=False):
        """
        获取元素对象
        """
        uid = None
        if self.iframe != self.web_page:
            html_ele = self.read_ele(self.uids, "html")
            code = """
            function (element, xpath) {
                
                $x = (xpath) => {
                    try {
                        let xpathResult = document.evaluate(xpath, element, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); 
                        let nodes = []
                        let node = null               
                        while (node = xpathResult.iterateNext()) {
                            nodes.push(node)
                        }
                        return nodes
                    } catch (error) {
                        return []
                    }

                }                
                let eles = $x(xpath)
                
                if (eles.length > 1) {
                    return ["", "", "找到多个元素,无法唯一定位"]
                }
                if (eles.length == 0) {
                    return ["", "", "未找到元素"]
                }
                let ele = eles[0]
                
                let uid = ele.getAttribute("diy-uid")
                if (uid == null || uid == "") {
                    uid = new Date().getTime().toString()
                    ele.setAttribute("diy-uid", uid)
                }
                return [uid, ele.tagName, "成功"]
            }
            """
            uid, tag_name, msg = html_ele.execute_javascript(code, xpath)
            if msg != "成功":
                raise Exception(msg)

            ele = self.read_ele(self.uids, tag_name, uid)
        else:
            ele = self.web_page.find_by_xpath(xpath, timeout=1)
            uid = get_uid()
            ele.set_attribute("diy-uid", uid)
        
        if is_iframe:
            self.uids.append(uid)

        return ele


    def find_ele2(self, xpath):
        """
        获取元素对象-跨多层iframe
        """
        start_time = time.time()
        iframe_page_list = self.find_all_iframe()
        end_time = time.time()      
        eles = []
        start_time = time.time()

     
        for iframe_page in iframe_page_list:
            try:
                ele = iframe_page.find_ele(xpath)
                eles.append(ele)
            except:
                pass
        end_time = time.time()
        if len(eles) == 1:
            return eles[0]
        if len(eles) == 0:
            raise Exception("未找到元素")
        raise Exception("找到多个元素,无法唯一定位")

    # @record_time
    def find_all_iframe(self):
        """基于当前iframe 查找所有后代 iframe (包含自身)"""
        uids = self.uids.copy()
        if len(self.uids) == 0:
            iframe_eles = self.web_page.find_all_by_xpath("//iframe", timeout=0.1)
        else:      
            iframe_eles = self.read_ele(uids,"iframe",None,False)
        
        iframe_page_list = [self]
        self._find_all_iframe(iframe_eles, uids, iframe_page_list)
        return iframe_page_list     


    def _find_all_iframe(self, iframe_eles, uids, iframe_page_list):
        if len(iframe_eles) == 0:
            return
        for iframe_ele in iframe_eles:
            uid = iframe_ele.get_attribute("diy-uid")
            print("id", iframe_ele.get_attribute("id"))
            if uid is None or uid == "":
                uid = get_uid()
                iframe_ele.set_attribute("diy-uid", uid)
            uids.append(uid) 
            iframe_page = IframePage(self.web_page, iframe_ele, uids.copy())
            iframe_page_list.append(iframe_page)

            iframe_eles = self.read_ele(uids,"iframe",None,False)
            
            self._find_all_iframe(iframe_eles, uids, iframe_page_list)
            uids.pop(-1)


    def find_all_ele(self, xpath, find_descendant_iframe=False):
        """
        获取相似元素列表
        """ 
        eles = []
        code = """
        function (element, xpath) {
            function $x(xpath) {
                let xpathResult = document.evaluate(xpath, element, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); 
                let nodes = []
                let node = null               
                while (node = xpathResult.iterateNext()) {
                    nodes.push(node)
                }
                return nodes     
            }

            function get_uid(uids) {
                let uid = new Date().getTime().toString()
                while (uids.includes(uid)) {
                    uid += "0"
                }
                return uid
            }         
            let uids = []
            ele_info = []                
            eles = $x(xpath)
            for (let i = 0; i < eles.length; i++) {
                let ele = eles[i]
                let uid = ele.getAttribute("diy-uid")
                if (uid == null || uid == "") {
                    uid = get_uid(uids)
                    ele.setAttribute("diy-uid", uid)
                }
                uids.push(uid)
                ele_info.push([uid, ele.tagName])
            }
            return ele_info
        }
        """

        if find_descendant_iframe:
            total = 0
            eles = []
            list_iframe_page = self.find_all_iframe()            
            for iframe_page in list_iframe_page:
                temp_eles = iframe_page.find_all_ele(xpath)
                if len(temp_eles) != 0:
                    total += 1
                    eles = temp_eles
                if total > 1:
                    raise Exception("在多个iframe中找到相似元素,无法唯一定位")
            return eles


        if self.iframe != self.web_page:
            uids = self.uids.copy()
            html_ele = self.read_ele(uids,"html")
            ele_info = html_ele.execute_javascript(code, xpath)
            for uid, tag_name in ele_info:
                ele = self.read_ele(uids, tag_name, uid)
                eles.append(ele)
        else:
            eles = self.web_page.find_all_by_xpath(xpath, timeout=3)
        return eles



def main(args):
    web_page = xbot.web.get("*E*", mode='edge', use_wildcard=True)

    iframe_page = IframePage(web_page)
    # //*[@id="s_filter_frame"]
    # iframe_page = iframe_page.to_iframe('//*[@id="form1"]/div[7]/div[2]/table/tbody/tr/td[1]/iframe', find_descendant_iframe=True)
    # iframe_page.find_ele('//*[@id="as_id"]')


    # ###### 示例一
    # # 初始化iframe处理器
    # iframe_page = IframePage(web_page)

    # # 切换iframe
    # iframe_page = iframe_page.to_iframe('//*[@id="QQMailSdkTool_login_loginBox_qq"]/iframe')
    # iframe_page = iframe_page.to_iframe('//*[@id="ptlogin_iframe"]')

    # # 查找元素
    # name_ele = iframe_page.find_ele('//*[@id="u"]')
    # name_ele.input("23124325")


    # ###### 示例二
    # # 初始化iframe处理器
    # iframe_page = IframePage(web_page)

    # # 跨层级切换iframe
    # iframe_page = iframe_page.to_iframe('//*[@id="ptlogin_iframe"]', find_descendant_iframe=True)

    # # 查找元素
    # name_ele = iframe_page.find_ele('//*[@id="u"]')


    # ###### 示例三
    # # 初始化iframe处理器
    # iframe_page = IframePage(web_page)

    # # 跨层级查找元素
    name_ele = iframe_page.find_ele2('//*[@id="o_id"]').click()



    pass
复制成功