2014-02-25 106 views
11

我是PhantomJS的新手。我想加載一個頁面,刮掉它的鏈接,然後按順序打開每個頁面,每次打開一個頁面,甚至可能在每個請求之間有一段延遲。我很難讓一個人在另一個之後開火,所以我想也許我可以使用承諾來解決這個問題,但我不認爲Node庫可以與Phantom一起工作。我到目前爲止看到的每個例子都會打開一個頁面,然後退出。PhantomJS的Promise框架?

下面是我有:

var page = require('webpage').create(); 

page.open('http://example.com/secretpage', function(status) { 
    console.log(status); 
    if(status !== 'success') { 
     console.log('Unable to access network'); 
    } else { 
     var links = page.evaluate(function() { 
      var nodes = []; 
      var matches = document.querySelectorAll('.profile > a'); 
      for(var i = 0; i < matches.length; ++i) { 
       nodes.push(matches[i].href); 
      } 
      return nodes; 
     }); 


     links.forEach(function(link) { 
      console.log(link); 
      page.open(link, function(status) { // <---- tries opening every page at once 
       console.log(status); 

       var name = page.evaluate(function() { 
        return document.getElementById('username').innerHTML; 
       }); 

       console.log(name); 
       page.render('profiles/'+name + '.png'); 
      }); 
     }); 
    } 
// phantom.exit(); 
}); 

有沒有一種方法,我可以按順序打開每一個環節?

回答

4

對於這種典型的情況,我使用async.js,特別是隊列component

這裏是一個非常基本的實施

phantom.injectJs('async.js'); 

var q = async.queue(function (task, callback) { 
    page.open(task.url, function(status) { // <---- tries opening every page at once 
       if(status !== 'success') { 
     console.log('Unable to open url > '+task.url); 
    } else { 
       console.log('opened '+task.url); 
       //do whatever you want here ... 
        page.render(Date.now() + '.png'); 
       }   
       callback(); 
      }); 

}, 1); 

// assign a callback 
q.drain = function() { 
    console.log('all urls have been processed'); 
    phantom.exit(); 
} 

var page = require('webpage').create(); 

page.open('http://phantomjs.org/', function(status) { 
    console.log(status); 
    if(status !== 'success') { 
     console.log('Unable to access network'); 
    } else { 
     var links = page.evaluate(function() { 
      var nodes = []; 
      var matches = document.querySelectorAll('a'); 
      for(var i = 0; i < matches.length; ++i) { 
       nodes.push(matches[i].href); 
      } 
      return nodes; 
     }); 

     links.forEach(function(link) { 
       q.push({url: link}, function (err) { 
        console.log('finished processing '+link); 
       }); 
     }); 
    } 
}); 

URL被添加到隊列,並且將並行地處理(直到併發限制,一個在這裏)。我重用了相同的頁面實例,但這不是強制性的。

正如我已經在過去做了這種履帶式的,讓我給你兩個建議:

  • 不要加載圖像,以加快測試
  • href是有時相對的,所以首先檢查如果它是一個有效的URL
+0

當我嘗試做phantom.injectJs('async.js')時,我得到一個錯誤:ReferenceError:無法找到變量:出口....有關如何AMD是在async.js中實現的嗎?這種情況發生在phantomjs 1.9.8和phantomjs 2 –

4

[編輯]

您需要排隊此。我修改了你的代碼並在其中添加了一個簡單的隊列機制。

var page = require('webpage').create(); 

page.open('http://example.com/secretpage', function(status) { 
    console.log(status); 
    if (status !== 'success') { 
     console.log('Unable to access network'); 
    } else { 
     var links = page.evaluate(function() { 
      var nodes = []; 
      var matches = document.querySelectorAll('.profile > a'); 
      for (var i = 0; i < matches.length; ++i) { 
       nodes.push(matches[i].href); 
      } 
      return nodes; 
     }); 

     var pointer = 0, 
      linksCount = links.length, 
      q = function() { 
       var link = links[pointer]; 
       console.log(link); 

       page.open(link, function(status) { // <---- tries opening every page at once 
        console.log(status); 

        var name = page.evaluate(function() { 
         return document.getElementById('username').innerHTML; 
        }); 

        console.log(name); 
        page.render('profiles/' + name + '.png'); 

        // pointer increaments; 
        pointer++; 
        if (pointer == linksCount) { 
         // recursion exit 
         phantom.exit(); 
        } 
        else { 
         // recursive cal; 
         q(); 
        } 
       });    

      }; 

     // start queue to load links one by one  
     q(); 
}); 

注意:foreach不會等待每個頁面加載和頁面加載是異步的。因此你的問題。

您可以閱讀代碼的CasperJS回答類似的問題(約PhantomJS包裝)如何應對這種來自How to for loop in casperjs

+0

雖然使用'async.js'作爲Cyber​​maxs的回覆--Betclic是絕對完美的,我承認。 – sudipto

2

你可以使用Phantom-promiseA PhantomJS bridge with a promise based api.phantomPhantomJS integration module for NodeJS。 其他選項依次打開每個環節

  1. Cyber​​maxs answer
  2. waitFor的建議Cyber​​maxs
  3. 使用例如在other SO question

基本上你有3個選擇,但你可以採取alook CasperjsNavigation scripting & testing for PhantomJS and SlimerJS