开发者问题收集

使用 PhantomJS 的 jQuery 错误

2016-07-08
195

我目前正在开发一个自动化测试脚本,它需要一个无头浏览器(PhantomJS)以及所有可能的 DOM 导航和操作(下载除外)。 因此 PhantomJS 应该可以满足要求。

但是我无法将 PhantomJS 与 javascript jQuery 一起使用,因为出现以下错误:

var page = require('webpage').create();
page.open('http://mywebsite.com/', function() {
    function dothat() {
        console.log('0 started');
        $('#id').children().eq(0).click();
        console.log('0 finished');
        return main(1, 0);
    }
}
dothat()

此处脚本停止在“0 已启动”。我加载的页面使用 jQuery,因此我认为不需要将其下载回来。即便如此,我们还是尝试加载它。

var page = require('webpage').create();
page.open('http://mywebsite.com/', function() {
    page.injectJs('jquery-2.2.1.min.js');
    function dothat() {
        console.log('0 started');
        $('#id').children().eq(0).click();
        console.log('0 finished');
        return main(1, 0);
    }
}
dothat()

是的,我在本地同一目录中有该文件... 并且它显示该错误(非常快):

TypeError: undefined is not a constructor (evaluating 'jQuery.easing[jQuery.easing.def](t,e,n,i,a)')

您建议如何修复该问题? 这是 PhantomJS 中的一个内部错误吗?所以我应该在 Ubuntu 上尝试使用 Firefox 和 Xvfb,还是我的某个错误? 我首先想到的是将代码更改为完全非 jQuery 版本,但我需要带有类的出色选择器并单击某些对象...

非常感谢您指出解决方案!

编辑 1:我根据对第一个答案的理解对此进行了更改。现在控制台不打印任何内容

var page = require('webpage').create();
page.onConsoleMessage = function(msg, lineNum, sourceId) {
    console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
page.open('http://mywebsite.com/', function() {
    page.injectJs('jquery-2.2.1.min.js');
    var result = page.evaluate(function() {
        function dothat() {
            console.log('0 started');
            $('#id').children().eq(0).click();
            console.log('0 finished');
            return main(1, 0);
        }
        function main(x, y) {
            if (x === 0) {
                return setTimeout(dothat(), y);
            } else if (x === 1) {
                return setTimeout(dothis(), y);
            }
        }
        return main(0, 5000); //first launch
    }
});

但我不确定这是您指出的。

编辑 2:这是一个真正的脚本,它想要访问 google.com 并以我的身份登录。但是,该脚本无法按预期工作,请参见下文。

var page = require('webpage').create();
page.onConsoleMessage = function(msg) {
    console.log('CONSOLE: ' + msg);
};
page.open('https://google.com/', function() {
    page.injectJs('jquery-2.2.1.min.js');
    page.evaluate(function() {
        function dofirst() {
            $('#gb_70').click();
            return main(1, 0);
        }
        function dosecond() {
            document.getElementById('Email').value = '[email protected]';
            $('#next').click();
            return main(2, 0);
        }
        function dothird() {
            document.getElementById('Passwd').value = 'mypwd';
            $('#signIn').click();
        }
        function main(i, j) {
            if (i === 0) {
                console.log('launching 0');
                return setTimeout(dofirst(), j); // connections
            }
            else if (i === 1) {
                console.log('launching 1');
                return setTimeout(dosecond(), 5000);
            }
            else if (i === 2) {
                console.log('launching 2');
                return setTimeout(dothird(), 5000);
            }
        }
        return main(0, 5000);
    });
});

如您所见,main 是我用于延迟步骤的函数。 但是代码不起作用并返回此错误(或那些):

TypeError: null is not an object (evaluating 'document.getElementById('Email').value = '[email protected]'')

  undefined:7 in dosecond
  :22 in main
  :4 in dofirst
  :18 in main
  :29
  :30

感谢您一直努力帮助我!

2个回答

在 PhantomJS 中,始终存在两个 javascirpt 上下文,其中存在函数和变量:

  1. PhantomJS 上下文
  2. 打开的页面上下文

它们彼此不了解,并且不相交!

PhantomJS 无法访问任何目标页面对象或 DOM,这就是 $('#id').children().eq(0).click(); 不起作用的原因 - PhantomJS 脚本中没有 jQuery,没有 DOM 元素。它们位于第二个上下文中,或者换句话说, 沙盒

要对页面执行任何操作,您必须将代码传送到那里。这是在 page.evaluate() 方法的帮助下完成的。

var page = require('webpage').create();
page.open('http://mywebsite.com/', function() {
    page.injectJs('jquery-2.2.1.min.js');
    var result = page.evaluate(function(){

        // You can certainly use functions
        // Be sure to declare them also 
        // inside of page.evaluate 
        function dothat() {
            console.log('0 started');
            $('#id').children().eq(0).click();
            console.log('0 finished');
            return main(1, 0);
        }

        // Important: don't forget to return result
        // back to PhantomJS main context
        return dothat();
    });
});

请注意,要从沙盒页面上下文接收控制台消息,您必须通过 page.onConsoleMessage 回调订阅它们:

page.onConsoleMessage = function(msg, lineNum, sourceId) {
    console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};

编辑 1 之后: 我的错,我在编辑您的示例(上面已修复)时丢失了一个括号,这导致了语法错误,而 PhantomJS v2.x 不会对此提出抱怨,它只会静静地挂在那里。这就是您误以为没有控制台消息的原因。首先在 v1.9.8 中或在某些具有语法高亮功能的 IDE 中测试脚本的语法错误可能会很有用。

您当前的代码应如下所示(缺少的括号也已修复):

var page = require('webpage').create();
page.onConsoleMessage = function(msg, lineNum, sourceId) {
    console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
page.open('http://mywebsite.com/', function() {
    page.injectJs('jquery-2.2.1.min.js');
    var result = page.evaluate(function() {
        function dothat() {
            console.log('0 started');
            $('#id').children().eq(0).click();
            console.log('0 finished');
            return main(1, 0);
        }
        function main(x, y) {
            if (x === 0) {
                return setTimeout(dothat(), y);
            } else if (x === 1) {
                return setTimeout(dothis(), y);
            }
        }
        return main(0, 5000); //first launch
    }); // 
});

注意:将会抱怨不存在的 dothis() 函数。

Vaviloff
2016-07-09

你不想反过来做吗?

function dothat() {
    console.log('0 started');
    $('#id').children().eq(0).click();
    console.log('0 finished');
    return main(1, 0);
}

var page = require('webpage').create();
page.open('http://mywebsite.com/', dothat)
JonSG
2016-07-08