更快地执行废弃成千上万页的CasperJS脚本的最佳实践
我写了一个CasperJS脚本,除了需要(非常非常)很长的时间才能清除页面,其效果非常好。更快地执行废弃成千上万页的CasperJS脚本的最佳实践
概括地说,这里的伪代码:
- 我的功能报废元素
- 我
casper.start()
启动导航并登录 -
casper.then()
,我遍历数组和存储我链接 -
casper.thenOpen()
打开每个链接并调用我的函数来报废。
它工作完美(和足够快)的废除一堆链接。但是当涉及到数千个(现在我正在运行带有100K链接数组的脚本)时,执行时间是无止境的:第一个10K链接已经在3小时54分10秒内被报废,接下来的10K在2小时18分27秒内被报废。
我可以解释两个10K批次之间的差异:第一个包括循环&与100K链接数组的存储。从这一点来看,脚本只会打开页面来取消它们。不过,我注意到阵列已经准备好了,大概需要30分钟,所以它不能准确解释时间间隔。
我已经将我的casper.thenOpen()
放在for循环中,希望在构建并存储在数组中的每个新链接之后都会发生报废。现在,我确定我已经失败了,但它会改变性能方面的任何事情吗?
这是我现在唯一想到的领导,如果有人愿意分享他/她的最佳实践,以显着减少脚本执行的运行时间(应该不难!)。 。
编辑#1
这里是我下面的代码:
var casper = require('casper').create();
var fs = require('fs');
// This array maintains a list of links to each HOL profile
// Example of a valid URL: https://myurl.com/list/74832
var root = 'https://myurl.com/list/';
var end = 0;
var limit = 100000;
var scrapedRows = [];
// Returns the selector element property if the selector exists but otherwise returns defaultValue
function querySelectorGet(selector, property, defaultValue) {
var item = document.querySelector(selector);
item = item ? item[property] : defaultValue;
return item;
}
// Scrapping function
function scrapDetails(querySelectorGet) {
var info1 = querySelectorGet("div.classA h1", 'innerHTML', 'N/A').trim()
var info2 = querySelectorGet("a.classB span", 'innerHTML', 'N/A').trim()
var info3 = querySelectorGet("a.classC span", 'innerHTML', 'N/A').trim()
//For scrapping different texts of the same kind (i.e: comments from users)
var commentsTags = document.querySelectorAll('div.classComments');
var comments = Array.prototype.map.call(commentsTags, function(e) {
return e.innerText;
})
// Return all the rest of the information as a JSON string
return {
info1: info1,
info2: info2,
info3: info3,
// There is no fixed number of comments & answers so we join them with a semicolon
comments : comments.join(' ; ')
};
}
casper.start('http://myurl.com/login', function() {
this.sendKeys('#username', 'username', {keepFocus: true});
this.sendKeys('#password', 'password', {keepFocus: true});
this.sendKeys('#password', casper.page.event.key.Enter, {keepFocus: true});
// Logged In
this.wait(3000,function(){
//Verify connexion by printing welcome page's title
this.echo('Opened main site titled: ' + this.getTitle());
});
});
casper.then(function() {
//Quick summary
this.echo('# of links : ' + limit);
this.echo('scraping links ...')
for (var i = 0; i < limit; i++) {
// Building the urls to visit
var link = root + end;
// Visiting pages...
casper.thenOpen(link).then(function() {
// We pass the querySelectorGet method to use it within the webpage context
var row = this.evaluate(scrapDetails, querySelectorGet);
scrapedRows.push(row);
// Stats display
this.echo('Scraped row ' + scrapedRows.length + ' of ' + limit);
});
end++;
}
});
casper.then(function() {
fs.write('infos.json', JSON.stringify(scrapedRows), 'w')
});
casper.run(function() {
casper.exit();
});
在这一点上,我可能比答案更多的问题,但让我们来试试。
是否有一个特定的原因,你为什么使用CasperJS而不是卷曲例如?例如,如果您要刮取使用Javascript的网站,我可以理解对CasperJS的需求。或者你想要截图。否则,我可能会使用Curl以及PHP或Python之类的脚本语言,并利用内置的DOM解析函数。 你当然也可以使用Scrapy等专用刮板工具。有相当多的工具可用。
然后'明显'的问题:你真的需要有数组那么大?你试图达到的目标并不清楚,我假设你会想要将提取的链接存储到数据库或其他东西。是不是可以分批分批处理?
一两件事,应该帮助是通过声明固定大小的数组即分配足够的内存: var theArray = new Array(1000);
调整大小的数组不断,势必会导致性能问题。每次将新项目添加到阵列时,都必须在后台进行昂贵的内存分配操作,并且在循环运行时重复执行。
由于您没有显示任何代码,所以我们不能建议有意义的改进,只是泛泛而谈。
感谢您的回答@匿名!我没有考虑过Curl,因为我只知道PhantomJS,CasperJS和SlimmerJS是网络报废最流行的语言。基于你的回答,我认为Curl可能会做到这一点,所以我肯定会给它一个镜头。我使用import.io进行刮擦,但是我无法承受他们的计划,所以我决定自学自己的CasperJS。我需要抓取数十万个网页,所以在某些时候,我需要大型数组来存储URL。我没有在这里再分配内存,我也会尝试这个,并比较结果。我用我的代码更新了我的问题;) – DFATPUNK