• 《SeleniumBasic 3.141.0.0


    SeleniumBasic中的IWebDriver对象的ExecuteScript方法用于执行JavaScript脚本。语法如下

    Function ExecuteScript(script As String, [arg0], [arg1], [arg2])

    后面3个是可选参数。

    调用ExecuteScript大体分为两种情况:无返回值的和有返回值的。

    执行无返回值的外部使用Call关键字,例如:

    Call WD.ExecuteScript("alert('Hello,ryueifu!')")

    alert('Hello,ryueifu!')是JavaScript语法,作用是弹出一个对话框,无返回值。所以外侧使用VBA的Call

    另一种是把JavaScript代码执行的结果返回给VBA。例如下面的程序借助JavaScript计算数学题,结果返回给prod:

    Dim prod As Integer
    prod = WD.ExecuteScript("return 7-4*5")
    Debug.Print prod

    打印结果是-13

    注意:要想从ExecuteScript中得到返回值,在脚本中必须有return关键字!否则返回Nothing

    另外,SeleniumBasic的ExecuteScript函数,除了执行的脚本代码以外,还可以最多设置3个可选参数。这些参数与脚本代码中的arguments对号入座。这样可以增强程序的灵活性。

    例如:
    prod = WD.ExecuteScript("var p=1;p=arguments[2]-arguments[0]*arguments[1];return p", CStr(4), CStr(5), CStr(7))
    Debug.Print prod

    上述程序的Cstr(4)对应于arguments[0],以此类推。返回的结果仍然是-13。

    当然,同一个参数也可以在脚本中被多次访问,例如:

    Call WD.ExecuteScript("alert(arguments[0]+'就是'+arguments[0])", Application.UserName)

    Excel VBA中的用户名被调用了两次,显示在网页上。

    以上是执行JavaScript脚本的基本知识。

    下面介绍几个非常实用的JavaScript技术。

    • 利用JavaScript定位元素

    通过前面的学习,SeleniumBasic中的FindElement系列方法只能向下查找,也就是说只能查找已知元素包含的子孙元素。而不能得到一个元素的父级、兄弟元素。实际上这个结论是不对的。FindElement系列的8个定位方法,其中根据XPath、CssSelector这两个定位方法,可以用来定位父级、兄弟元素。

    不过本节要介绍的是通过JavaScript代码来定位,其理论依据请参考

    • https://www.w3school.com.cn/jsref/dom_obj_document.asp
    • https://www.w3school.com.cn/jsref/dom_obj_all.asp

    因为JavaScript可以遍历和定位DOM中的元素,SeleniumBasic又可以执行JavaScript脚本,因此可以实现。

    下面以遍历百度首页左上角的7个超链接为例讲解。在开发工具中看一下HTML的构成。

    可以看到这7个超链接,位于一个div中。因此,先定位到div,然后找到它的第一个儿子、最后一个儿子,前一个儿子后一个儿子,最后一句parentNode返回其儿子的父亲,也就是他本人。

        Dim div As SeleniumBasic.IWebElement
        Dim anchor As IWebElement
        Set div = WD.ExecuteScript("return document.getElementById('s-top-left')")      
        Set anchor = WD.ExecuteScript("return arguments[0].firstChild", div)
        Debug.Print anchor.text
        Set anchor = WD.ExecuteScript("return arguments[0].lastChild", div)
        Debug.Print anchor.text
        Set anchor = WD.ExecuteScript("return arguments[0].previousSibling", anchor)
        Debug.Print anchor.text
        Set anchor = WD.ExecuteScript("return arguments[0].nextSibling", anchor)
        Debug.Print anchor.text
        Set anchor = WD.ExecuteScript("return arguments[0].parentNode", anchor)
        Debug.Print anchor.text

    以上代码中的5个打印语句的结果如下:

     注意领会理解上述代码的精髓,ExecuteScript方法中的参数arguments[0]接收的是一个IWebElement对象,然后该函数返回的结果是另一个IWebElement。

    通过这个实例,可以看出定位元素毫无问题。

    • 获取和修改网页元素的属性

    SeleniumBasic的IWebElement具有GetAttribute方法,可以返回指定属性名称的属性值。

    但是JavaScript中的网页元素都有getAttribute和setAttribute,既可以返回又可以设置属性。

    众所周知,百度的搜索按钮的HTML定义如下:

    <input type="submit" id="su" value="百度一下" class="bg s_btn">

    在VBA中运行

    Set button = WD.FindElementById("su")

    Debug.Print WD.ExecuteScript("return arguments[0].getAttribute(arguments[1])", button, "value")

    就可以得到它的value属性:百度一下。

    同理,运行

    Call WD.ExecuteScript("arguments[0].setAttribute(arguments[1],arguments[2])", button, "value", "百度两下")
    可以修改其属性,造成按钮文字被修改。

    上面这行代码用了3个arguments,把ExecuteScript的用法演绎的淋漓尽致。如果不使用参数,纯粹的JavaScript代码是:

    Call WD.ExecuteScript("document.getElementById('su').setAttribute('value','百度三下')")

    注意:JavaScript中关键字和函数名严格区分大小写。

    • 调用网页元素的函数和方法

    JavaScript中网页元素有众多的函数和方法。

    例如下面的程序,自动输入关键字中秋节,自动点击“百度一下”按钮。

    WD.URL = "https://www.baidu.com"
    WD.ExecuteScript "document.getElementById('kw').value='中秋节'"
    WD.ExecuteScript "document.getElementById('su').click()"

    也可以把程序中的对象作为参数传递进去,例如下面的程序,把keyword和button传递到脚本中,通过执行脚本就实现了关键字的输入和搜索按钮的点击。

        WD.URL = "https://www.baidu.com"
        Dim form As SeleniumBasic.IWebElement
        Dim keyword As SeleniumBasic.IWebElement
        Dim button As SeleniumBasic.IWebElement
        Set form = WD.FindElementById("form")
        Set keyword = form.FindElementById("kw")
        Set button = form.FindElementById("su")
        WD.ExecuteScript "arguments[0].value='SeleniumBasic';arguments[1].submit()", keyword, button

    运行到ExecuteScript那句时,浏览器的样子如下:

    另外,HTMLDocument中有InnerHTML和OuterHTML这两个概念,用来返回网页元素的HTML定义。SeleniumBasic中只提供了IWebElement的TagName、Text等,无法返回完整的HTML标签定义。

    Set button = WD.FindElementById("su")

    Debug.Print WD.ExecuteScript("return arguments[0].outerHTML", button)

    以上代码的打印结果是:

    <input type="submit" id="su" value="百度三下" class="bg s_btn">

    而下面这句则返回按钮的父级元素的定义

    Debug.Print WD.ExecuteScript("return arguments[0].parentNode.outerHTML", button)

    结果为:

    <span class="bg s_btn_wr"><input type="submit" id="su" value="百度三下" class="bg s_btn"></span>
    • 操作DOM文档对象

    JavaScript中的document对象是网页文档的最顶级对象。各种成员请参考https://www.w3school.com.cn/jsref/dom_obj_document.asp

    下面仅仅举一个返回网页当前的Cookie

    Debug.Print WD.ExecuteScript("return document.cookie")

    总之,JavaScript脚本无处不在,SeleniumBasic中通过ExecuteScript方法既可以把VBA的数据提交到网页,也可以把网页信息获取到VBA中。

    • 追记:获取复数个网页元素

    JavaScript中的document对象以及网页元素对象,具有getElementsByTagName等方法(注意复数s),可以返回具有多个元素的集合。

    百度首页的form元素的HTML定义如下

    可以看到form下面包含很多input标签。下面程序中的ExecuteScript函数返回一个IWebElement数组。在循环中打印每个input的HTML定义。

        Set form = WD.FindElementById("form")
        Dim Elements() As IWebElement
        Elements = WD.ExecuteScript("return arguments[0].getElementsByTagName('input')", form)
        Erase Elements
        Elements = form.FindElementsByTagName("input")
        For i = 0 To UBound(Elements)
            Debug.Print WD.ExecuteScript("return arguments[0].outerHTML", Elements(i))
        Next i

    打印结果为:

    <input type="hidden" name="ie" value="utf-8" style="">
    <input type="hidden" name="f" value="8" style="">
    <input type="hidden" name="rsv_bp" value="1">
    <input type="hidden" name="rsv_idx" value="1">
    <input type="hidden" name="ch" value="">
    <input type="hidden" name="tn" value="baidu">
    <input type="hidden" name="bar" value="">
    <input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
    <input type="submit" id="su" value="百度一下" class="bg s_btn">
    <input type="hidden" name="rn" value="">
    <input type="hidden" name="fenlei" value="256">
    <input type="hidden" name="oq" value="">
    <input type="hidden" name="rsv_pq" value="bd4732b1000713cd">
    <input type="hidden" name="rsv_t" value="4529JtCsqjSZIHJEyqM91vIrGVSEiU8pKf4IB+GlY7LKoyYC6BjG63llixk">
    <input type="hidden" name="rqlang" value="cn">
    <input type="hidden" name="rsv_enter" value="1">
    <input type="hidden" name="rsv_dl" value="ib" style="">

  • 相关阅读:
    Codeforces Round #370 (Div. 2) C. Memory and De-Evolution 水题
    Codeforces Round #370 (Div. 2) B. Memory and Trident 水题
    Codeforces Round #370 (Div. 2) A. Memory and Crow 水题
    Codeforces Round #258 (Div. 2) E. Devu and Flowers 容斥
    Codeforces Round #258 (Div. 2) D. Count Good Substrings 水题
    Codeforces Round #258 (Div. 2) C. Predict Outcome of the Game 水题
    Codeforces Round #258 (Div. 2) . Sort the Array 贪心
    Codeforces Round #258 (Div. 2) A. Game With Sticks 水题
    Codeforces Beta Round #14 (Div. 2) D. Two Paths 树形dp
    Codeforces Beta Round #14 (Div. 2) C. Four Segments 水题
  • 原文地址:https://www.cnblogs.com/ryueifu-VBA/p/13696693.html
Copyright © 2020-2023  润新知