2017-09-22 55 views
0

我寫了一個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(); 
}); 

回答

0

在這一點上,我可能比答案更多的問題,但讓我們來試試。

是否有一個特定的原因,你爲什麼使用CasperJS而不是捲曲例如?例如,如果您要刮取使用Javascript的網站,我可以理解對CasperJS的需求。或者你想要截圖。否則,我可能會使用Curl以及PHP或Python之類的腳本語言,並利用內置的DOM解析函數。 你當然也可以使用Scrapy等專用刮板工具。有相當多的工具可用。

然後'明顯'的問題:你真的需要有數組那麼大?你試圖達到的目標並不清楚,我假設你會想要將提取的鏈接存儲到數據庫或其他東西。是不是可以分批分批處理?

一兩件事,應該幫助是通過聲明固定大小的數組即分配足夠的內存var theArray = new Array(1000);

調整大小的數組不斷,勢必會導致性能問題。每次將新項目添加到陣列時,都必須在後臺進行昂貴的內存分配操作,並且在循環運行時重複執行。

由於您沒有顯示任何代碼,所以我們不能建議有意義的改進,只是泛泛而談。

+0

感謝您的回答@匿名!我沒有考慮過Curl,因爲我只知道PhantomJS,CasperJS和SlimmerJS是網絡報廢最流行的語言。基於你的回答,我認爲Curl可能會做到這一點,所以我肯定會給它一個鏡頭。我使用import.io進行刮擦,但是我無法承受他們的計劃,所以我決定自學自己的CasperJS。我需要抓取數十萬個網頁,所以在某些時候,我需要大型數組來存儲URL。我沒有在這裏再分配內存,我也會嘗試這個,並比較結果。我用我的代碼更新了我的問題;) – DFATPUNK