一般注意事项
最快的选择器将是 id 选择器,但即使您的 id 位于树的更高位置,它们也不会为您提供太多。正如 Ian 在 comments 中指出的那样,选择器是从右到左解析/评估的。这意味着引擎会查找所有具有匹配属性的输入,即使它只有一个,然后才向上搜索树以查看前面的元素是否匹配。
我发现如果您知道输入在哪个封闭元素中,您可以使用 JavaScript DOM 属性遍历 DOM 并在树的较小部分运行 querySelector。至少在我的测试中,这将时间减少了一半以上。
内存问题
从您更新的问题来看,似乎确实是内存问题。当您拥有数十万个元素时,相对较旧的 PhantomJS WebKit 引擎会尝试分配足够的内存。当它占用的内存超过可用内存甚至超过您的机器拥有的内存时,操作系统会通过使用硬盘上的交换内存进行补偿。
当您的脚本尝试查询当前仅处于交换状态的元素时,此查询需要很长时间,因为它必须从与内存相比非常慢的高延迟硬盘中获取数据。
我的测试运行 100k 个表单,每个表单有一个元素,每次查询不到 30 毫秒。当我增加元素数量时,执行时间线性增长,直到某个时候我得到(registering to onError)
运行时错误 R6016
- 没有足够的空间用于线程数据
所以我无法重现您在 Windows 上每次查询需要 3-5 秒的问题。
可能的解决方案
1。更好的硬件:
尝试在内存更大的机器上运行它,看看它是否运行得更好。
2。通过关闭不必要的应用程序来减少使用的内存
3。操作页面以减少内存占用:
如果您不需要测试页面的某些部分,您可以在运行测试之前将它们从 DOM 中删除。如果您需要全部测试,您可以在同一页面上运行多个测试,但每次都删除当前未测试的所有内容。
Don't load images 如果这是一个图像重的网站,请设置casper.options.pageSettings.loadImages = false;。
测试脚本
var page = require('webpage').create();
var content = "",
max = 100000,
i;
for(i = 0; i < max; i++) {
content += '<form id="f' + i + '"><input type="hidden" name="in' + i + '" valuate"iv' + i + '"></form>';
}
page.evaluate(function(content){
document.body.innerHTML = content;
}, content);
console.log("FORMS ADDED");
setTimeout(function(){
var times = page.evaluate(function(max){
var obj = {
cssplain: 0,
cssbyForm: 0,
cssbyFormChild: 0,
cssbyFormJsDomChild: 0,
cssbyFormChildHybridChild: 0,
cssbyFormHybridChild: 0,
xpathplain: 0,
xpathbyForm: 0
},
idx, start, el, i,
repeat = 100;
function runTest(name, obj, test) {
var idx = Math.floor(Math.random()*max);
var start = (new Date()).getTime();
var el = test(idx);
obj[name] += (new Date()).getTime() - start;
return el;
}
for(i = 0; i < repeat; i++){
runTest('cssplain', obj, function(idx){
return document.querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
});
runTest('cssbyForm', obj, function(idx){
return document.querySelector('#f'+idx+' input[name="in'+idx+'"][value="iv'+idx+'"]');
});
runTest('cssbyFormChild', obj, function(idx){
return document.querySelector('form:nth-child('+(idx+1)+') input[name="in'+idx+'"][value="iv'+idx+'"]');
});
runTest('cssbyFormJsDomChild', obj, function(idx){
return document.body.children[max-1].querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
});
runTest('cssbyFormChildHybridChild', obj, function(idx){
return document.querySelector('form:nth-child('+(idx+1)+')').querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
});
runTest('cssbyFormHybridChild', obj, function(idx){
return document.querySelector('#f'+idx).querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
});
runTest('xpathplain', obj, function(idx){
return document.evaluate('//input[@name="in'+idx+'" and @value="iv'+idx+'"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
});
runTest('xpathbyForm', obj, function(idx){
return document.evaluate('//form[@id="f'+idx+'"]//input[@name="in'+idx+'" and @value="iv'+idx+'"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
});
}
for(var type in obj) {
obj[type] /= repeat;
}
return obj;
}, max);
console.log("TIMES");
for(var type in times) {
console.log(type+":\t"+times[type]);
}
phantom.exit();
}, 0); // just in case the content is not yet evaluated
在我的机器上输出(更好):
cssbyForm:29.55
cssbyFormChild:29.97
cssbyFormChildHybridChild:11.51
cssbyFormHybridChild:10.17
cssbyFormJsDomChild:11.73
cssplain:29.39
xpathbyForm:206.66
xpathplain:207.05
注意:我直接使用了 PhantomJS。在 CasperJS 中使用相同的技术时,它不应该有不同的结果。