使用 PhantomJS 的 jQuery 错误
我目前正在开发一个自动化测试脚本,它需要一个无头浏览器(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
感谢您一直努力帮助我!
在 PhantomJS 中,始终存在两个 javascirpt 上下文,其中存在函数和变量:
- PhantomJS 上下文
- 打开的页面上下文
它们彼此不了解,并且不相交!
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() 函数。
你不想反过来做吗?
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)