快速执行刮擦数千页的CasperJS脚本的最佳实践

时间:2017-09-22 18:56:24

标签: arrays performance time casperjs execution

我编写了一个CasperJS脚本,除了需要(非常非常)很长的时间来擦除页面之外,它的效果非常好。

简而言之,这是伪代码:

  1. 我的功能是刮掉元素
  2. 我的casper.start()开始导航并登录
  3. casper.then()我遍历一个数组并存储我的链接
  4. casper.thenOpen()打开每个链接并调用我的功能进行报废。
  5. 它可以完美地(并且足够快)用于抓取一堆链接。但是当涉及到数千个(现在我正在运行带有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();
    });
    

1 个答案:

答案 0 :(得分:0)

此时我可能有更多的问题而不是答案,但试试吧。

您是否有特殊原因要求使用CasperJS而不是Curl?如果你要刮一个使用Javascript的网站,我可以理解CasperJS的必要性。或者你想拍摄截图。否则我可能会使用Curl以及PHP或Python等脚本语言,并利用内置的DOM解析功能。 当然,您可以使用像Scrapy这样的专用刮擦工具。有很多工具可用。

然后显而易见的&#39;问题:你真的需要数组那个大吗?你想要实现的目标尚不清楚,我假设你想要将提取的链接存储到数据库或其他东西。是不是可以小批量分割过程?

通过声明固定大小的数组,可以帮助分配足够的内存,即: var theArray = new Array(1000);

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

由于您没有显示任何代码,因此我们无法提出有意义的改进,只是一般性。