开发者问题收集

从 Java Selenium 执行 JavaScript 库和函数

2019-08-05
659

我在 Eclipse 中编写 Java GUI/脚本,运行无头 chromedriver,尝试在驱动程序中加载 html2canvas.js [1] [2] 库,然后在浏览器中调用我在该库上编写的函数时遇到问题;我收到以下代码的未定义错误:

String ss1ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\html2canvas.min.js";
String ss2ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\takeScreenShot.js";

je.executeScript(
        "var headID1 = document.getElementsByTagName('head')[0]; "
        + "var newScript1 = document.createElement('script'); "
        + "newScript1.type = 'text/javascript'; "
        + "newScript1.src = '" + ss1ScriptLoc + "'; "
        + "headID1.appendChild(newScript1); "
        + "var headID2 = document.getElementsByTagName('head')[0]; "
        + "var newScript2 = document.createElement('script'); "
        + "newScript2.type = 'text/javascript'; "
        + "newScript2.src = '" + ss2ScriptLoc + "'; "
        + "headID2.appendChild(newScript2); "
        + "$(document).ready( function () { takeScreenShot(); });"
    );

这导致未定义函数“takeScreenShot();”的错误。我以为我已经在本地 .js 文件中定义了它。

Starting ChromeDriver 75.0.3770.140 (-refs/branch-heads/3770@{#1155}) on port 48415
Only local connections are allowed.
Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code.
Aug 05, 2019 9:36:53 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
java.util.concurrent.ExecutionException: org.openqa.selenium.JavascriptException: javascript error: takeScreenShot is not defined
  (Session info: headless chrome=75.0.3770.142)
Caused by: org.openqa.selenium.JavascriptException: javascript error: takeScreenShot is not defined
  (Session info: headless chrome=75.0.3770.142)
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:25:48'
System info: host: '', ip: '', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_191'
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 75.0.3770.142, chrome: {chromedriverVersion: 75.0.3770.140 (2d9f97485c7b..., userDataDir: C:\Users\me\AppData\Lo...}, goog:chromeOptions: {debuggerAddress: localhost:55906}, javascriptEnabled: true, networkConnectionEnabled: false, pageLoadStrategy: normal, platform: XP, platformName: XP, proxy: Proxy(), setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: dismiss and notify}
Session ID: 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.openqa.selenium.remote.http.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:187)
    at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:122)
    at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:49)
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:158)
    at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:83)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:552)
    at org.openqa.selenium.remote.RemoteWebDriver.executeScript(RemoteWebDriver.java:489)
    at App.getScreenShot(App.java:170)
    at javax.swing.SwingWorker$1.call(SwingWorker.java:295)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at javax.swing.SwingWorker.run(SwingWorker.java:334)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

edit1:添加了此代码片段,经过测试,可以运行,但似乎并没有真正执行任何操作。

je.executeScript("");
je.executeScript("document.body.onload = myCustomFunc();"
    + "function myCustomFunc() { console.log('hello world'); }"
);

我获取了两个库 .js 文件并将它们读入 List,然后创建了一个大的 StringBuilder,将所有元素附加到 StringBuilder,然后添加了 takeScreenShot() 函数来代替 myCustomFunc()。不会抛出任何错误,但也不会生成下载的屏幕截图文件。


edit2:我的加载函数

private String fileToJSString(final String inScriptFile) {
        Path p = Paths.get(inScriptFile);
        if (!Files.exists(p)) {
            System.err.println("fileToJSString : error, file does not exist");
            return null;
        }
        List<String> lines = null;
        try {
            lines = Files.readAllLines(Paths.get(inScriptFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (lines == null) {
            System.err.println("fileToJSString : error, no lines read from file");
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (String s : lines)
            sb.append(s.trim() + " ");
        return sb.toString();
    }

edit3:根据@jay-mattinson的建议,我尝试加载脚本,但是运行时字符串变量不适用于html2canvas.js库(太多特殊字符 - 请参阅本文前面/上面的实际文件链接)

String ss1ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\html2canvas.min.js";
String ss2ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\takeScreenShot.js";
String jsScript1 = fileToJSString(ss1ScriptLoc);
String jsScript2 = fileToJSString(ss2ScriptLoc);
JavascriptExecutor je = (JavascriptExecutor) this.driver;
je.executeScript(
    "var headID = document.getElementsByTagName('head')[0]; "
    + "var newScript = document.createElement('script'); "
    + "newScript.type = 'text/javascript'; "
    + "var code = {" + jsScript1 + " " + jsScript2 + "};"
    + "newScript.appendChild(document.createTextNode(code));"
    + "headID.appendChild(newScript); "
    + "$(document).ready( function () { takeScreenShot(); });"
);

运行时抛出特定错误;

Caused by: org.openqa.selenium.WebDriverException: unknown error: Runtime.evaluate threw exception: SyntaxError: Unexpected token !

edit4:这是我编写的基于html2canvas的函数,用于抓取网页上表格的图像;

function takeScreenShot() {
    if (document.getElementsByClassName("abc-top") == null) {
        console.log('not on the right web page with the right content. stopping script');
        return;
    }

    var hiddenLinkObj = document.createElement('a');
    hiddenLinkObj.setAttribute('href', '');
    hiddenLinkObj.setAttribute('id', '');
    document.body.insertBefore(hiddenLinkObj, document.body.childNodes[0]);

    var s1 = window.location.href;
    var idx = s1.lastIndexOf('/');
    var res = s1.substring(idx + 1, s1.length);
    hiddenLinkObj.setAttribute('download', 's' + res + '-s.png');

    var partA = document.querySelector('[abc-show="showInfo"]');
    var partB = partA.getElementsByClassName('row');
    var partC = partB[3].childNodes[0];
    partC.setAttribute('style', 'background-color:#fafafa');
    var result = partC;

    html2canvas(
        result, {
            onrendered: function(canvas) {
                var myImage = canvas.toDataURL("image/png");
                hiddenLinkObj.setAttribute('href', myImage.replace("image/png", "image/octet-stream"));
                hiddenLinkObj.click();
            }
        }
    );
}

所以我的问题/问题: 为什么在定义和加载时会出现未定义的函数错误takeScreenShot.js 文件? - 如何加载一个大小合适的 html2canvas JS 库和一个包含我基于 html2canvas 库编写的单个 JS 函数的第二个/短库并在 selenium 中执行我的函数? -

我猜这是因为我缺乏 JavaScript 知识,而且我忽略了一个明显的错误。任何帮助都值得赞赏。

2个回答

以下是我使用 Selenium 执行/注入本地存储的“.js”文件的方法:

  File newJSFile = new File(path_to_local_js_file);
        if (newJSFile.exists())
        {
            try
            {
                  Scanner sc = new Scanner(new FileInputStream(newJSFile));
        String js_TxtFile = ""; 
            while (sc.hasNext()) {          
                String[] s = sc.next().split("\r\n");   
                for (int i = 0; i < s.length; i++) {
                    js_TxtFile += s[i];
                    js_TxtFile += " ";
                }   

            }
              try
                {
                ((JavascriptExecutor)driver).executeScript(js_TxtFile);
                }
                catch (Exception ex)
                {
                     System.out.println ("Exception when running Javascript: " + ex.toString());
                }

            }
            catch (Exception ex)
            {
            System.out.println(ex.toString());
            }
        }
browsermator
2019-08-05

出于安全原因,Chrome 不允许您加载本地资源。您需要将文件放置在驱动程序可访问的位置(网络驱动器/服务器/存储库等),或者读取文件并将其作为参数传递。

第二种方法的工作原理如下:无需在脚本元素上指定 src ,而是创建并附加一个文本节点,其中包含文件文本,这样就可以了:

je.executeScript(
    $"var headID1 = document.getElementsByTagName('head')[0]; "
    + "var newScript1 = document.createElement('script'); "
    + "newScript1.type = 'text/javascript'; "
    + "var code = {fileText}; "
    + "newScript1.appendChild(document.createTextNode(code)); "
    + "headID1.appendChild(newScript1); "
    + "takeScreenShot();"
);
MentallyRecursive
2019-08-05