我编写了一个CasperJS脚本,除了需要(非常非常)很长的时间来擦除页面之外,它的效果非常好。
简而言之,这是伪代码:
casper.start()
开始导航并登录casper.then()
我遍历一个数组并存储我的链接casper.thenOpen()
打开每个链接并调用我的功能进行报废。它可以完美地(并且足够快)用于抓取一堆链接。但是当涉及到数千个(现在我正在运行带有100K链接数组的脚本)时,执行时间是无止境的:前3K54m10s中的前10K链接已被废弃,而2h18m27s中的后10K链接已被废弃。
我可以解释一下两个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;
}
// Scraping 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 scraping 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 connection 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();
});
答案 0 :(得分:0)
此时我可能有更多的问题而不是答案,但试试吧。
您是否有特殊原因要求使用CasperJS而不是Curl?如果你要刮一个使用Javascript的网站,我可以理解CasperJS的必要性。或者你想拍摄截图。否则我可能会使用Curl以及PHP或Python等脚本语言,并利用内置的DOM解析功能。 当然,您可以使用像Scrapy这样的专用刮擦工具。有很多工具可用。
然后显而易见的&#39;问题:你真的需要数组那个大吗?你想要实现的目标尚不清楚,我假设你想要将提取的链接存储到数据库或其他东西。是不是可以小批量分割过程?
通过声明固定大小的数组,可以帮助分配足够的内存,即:
var theArray = new Array(1000);
不断调整阵列大小一定会导致性能问题。每次将新项添加到数组中时,必须在后台进行昂贵的内存分配操作,并在循环运行时重复。
由于您没有显示任何代码,因此我们无法提出有意义的改进,只是一般性。