更快地执行废弃成千上万页的CasperJS脚本的最佳实践

问题描述:

我写了一个CasperJS脚本,除了需要(非常非常)很长的时间才能清除页面,其效果非常好。更快地执行废弃成千上万页的CasperJS脚本的最佳实践

概括地说,这里的伪代码:

  1. 我的功能报废元素
  2. casper.start()启动导航并登录
  3. casper.then(),我遍历数组和存储我链接
  4. 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);

调整大小的数组不断,势必会导致性能问题。每次将新项目添加到阵列时,都必须在后台进行昂贵的内存分配操作,并且在循环运行时重复执行。

由于您没有显示任何代码,所以我们不能建议有意义的改进,只是泛泛而谈。

+0

感谢您的回答@匿名!我没有考虑过Curl,因为我只知道PhantomJS,CasperJS和SlimmerJS是网络报废最流行的语言。基于你的回答,我认为Curl可能会做到这一点,所以我肯定会给它一个镜头。我使用import.io进行刮擦,但是我无法承受他们的计划,所以我决定自学自己的CasperJS。我需要抓取数十万个网页,所以在某些时候,我需要大型数组来存储URL。我没有在这里再分配内存,我也会尝试这个,并比较结果。我用我的代码更新了我的问题;) – DFATPUNK