PhantomJS快速入門
本文簡要介紹了PhantomJS的相關基礎知識點,主要包括PhantomJS的介紹、下載與安裝、HelloWorld程序、核心模塊介紹等。由於鄙人才疏學淺,難免有疏漏之處,歡迎指正交流。
1、PhantomJS是什么?
PhantomJS是一個基於webkit的JavaScript API。它使用QtWebKit作為它核心瀏覽器的功能,使用webkit來編譯解釋執行JavaScript代碼。任何你可以在基於webkit瀏覽器做的事情,它都能做到。它不僅是個隱形的瀏覽器,提供了諸如CSS選擇器、支持Web標准、DOM操作、JSON、HTML5、Canvas、SVG等,同時也提供了處理文件I/O的操作,從而使你可以向操作系統讀寫文件等。PhantomJS的用處可謂非常廣泛,諸如網絡監測、網頁截屏、無需瀏覽器的 Web 測試、頁面訪問自動化等。
PhantomJS官方地址:http://phantomjs.org/。
PhantomJS官方API:http://phantomjs.org/api/。
PhantomJS官方示例:http://phantomjs.org/examples/。
PhantomJS GitHub:https://github.com/ariya/phantomjs/。
2、PhantomJS下載與安裝
官方下載地址:http://phantomjs.org/download.html。目前官方支持三種操作系統,包括windows\Mac OS\Linux這三大主流的環境。你可以根據你的運行環境選擇要下載的包,我的運行環境是Windows7。
下載完成后解壓文件,建議為方便使用,單獨放在一個文件夾里,如我放在D:\workspace\phantomjs里。
到這里,你已經成功下載安裝好PhantomJS了。那么,打開D:\workspace\phantomjs\bin文件夾,雙擊運行phantomjs.exe,出現如下界面,那么你就可以運行JS代碼了。
由於我們都比較懶,不喜歡為了運行一個程序總是跑到D:\workspace\phantomjs\bin文件夾打開phantomjs.exe。那么,你可以將phantomjs.exe添加到環境變量里。具體如下:打開我的電腦->右鍵屬性->高級系統設置->高級標簽->環境變量,在系統變量里找到Path,將你的phantomjs添加到環境變量里。比方說我的路徑添加的為“;D:\workspace\phantomjs\bin”,切記不要少了前面那個分號。
3、第一個PhantomJS小程序HelloWorld
好了,到目前為止,可以開始我們的第一個PhantomJS程序了。打開你的工作目錄,新建文件hello.js,敲入以下代碼,Ctrl+S保存:
1 // a phantomjs example 2 var page = require('webpage').create(); 3 phantom.outputEncoding="gbk"; 4 page.open("http://www.cnblogs.com/front-Thinking", function(status) { 5 if ( status === "success" ) { 6 console.log(page.title); 7 } else { 8 console.log("Page failed to load."); 9 } 10 phantom.exit(0); 11 });
然后,打開CMD命令行工具,切換到你的當前目錄,敲入phantomjs hello.js,結果如下:
如果你的結果跟我的一樣,那么恭喜你,你已經成功跑起來屬於你的第一個PhantomJS程序了。那么我們簡要介紹下上面的代碼:第2行,webpage是phantomjs的核心模塊之一,它給用戶提供了訪問、操作、選擇web文檔的接口。第3行,設置下編碼格式,否則輸出的可能是亂碼。第4行,運行page.open函數,其中第一個參數是你要訪問的url,第二個參數是一個回調函數。在回調函數里我們檢查了下返回的狀態,如果是success那么我們就將瀏覽的url制定文檔的title打印出來,如你所見,如果不是那么打印文檔加載出錯。最后一行退出phantomjs執行環境。
4、PhantomJS核心API
webpage:如你所見,上面的例子我們已經見識了它的威力了。它的作用主要是提供了一套可以訪問和操作web文檔的核心方法,包括操作DOM、事件捕獲、用戶事件模擬等等。
system:該模塊提供了一些與操作系統相關的接口,例如訪問操作系統信息、訪問系統環境變量、接受命令行參數等等與程序執行相關的系統信息。
fs:即FileSystem。熟悉NodeJS的朋友都知道,NodeJS也內建了相關的核心模塊。fs提供了執行文件I/O操作的標准接口,如讀寫文件、刪除文件等。它使得你持久化一些文件(如logfile等)變得非常容易。
webserver:如其名字一樣,你可以基於它來實現自己的webserver,用來處理請求並且執行PhantomJS代碼等。
其它一些配置信息,執行PhantomJS的命令格式如下:
1 phantomjs [switches] [options] [script] [argument [argument [...]]]
其中,各種參數都是可選的。例如我們第一個程序的執行命令如下:
1 phantomjs hello.js
打開debug模式(該模式用於開發,可提供必要提示信息):
1 phantomjs --debug=yes hello.js
設置cookie路徑:
1 phantomjs --cookie-file=cookie.txt hello.js
5、操作page content
在helloworld中我們已經學會了如何訪問一個url並取出它的title。下面我們看看如何選擇並操作DOM元素:
DOM選擇器,常用的getElementById、getElementByClassName、getElementByName、getElementByTagName、querySelector(CSS選擇器)。
我們看一個使用querySelector的例子:
1 var content = page.evaluate(function () { 2 var element = document.querySelector('#elem'); 3 return element.textContent; 4 }); 5 console.log(content);
evaluate函數是個新東西,其實很簡單,就是在webpage環境下執行evaluate傳入的回調函數,在這里面執行與phantom相關的操作可以避免web頁面刺探phantom相關的設置信息。上面的代碼就比較簡單了,不啰嗦了。
模仿用戶點擊事件:
phantomJS提供了兩種模仿點擊事件的接口,一個是sendEvent,phantomJS事件觸發器;一個是DOM事件觸發器。
我們先看看第一個,語法如下:
1 sendEvent( eventType, Point X, Point Y, button='left' ) 2 eventType: mouseup mousedown mousemove click doubleclick 3 Point X : 觸發事件的X坐標 4 Point Y: 觸發事件的Y坐標
第二個,我們都應該比較熟悉了:
1 var evt = document.createEvent("MouseEvents"); 2 evt.initMouseEvent( 3 "click", // 事件類型 4 true, 5 true, 6 window, 7 1, 8 1, 1, 1, 1, // 事件的坐標 9 false, // Ctrl鍵標識 10 false, // Alt鍵標識 11 false, // Shift鍵標識 12 false, // Meta鍵標識 13 0, // Mouse左鍵 14 element); // 目標元素 15 element.dispatchEvent(evt);
6、事件處理
在真正的瀏覽器里,任何事件發生都可見,而在PhantomJS里都是不可見的。在PhantomJS里,我們可以捕獲這些事件並做出相應處理。由於涉及到的事件有很多種,那么我們今天僅僅把一個比較有用的事件作為例子,基於這個事件你可以監控一個頁面並做出分析:
1 var startTime = null; 2 page.onLoadStarted = function() { 3 startTime = new Date().getTime(); 4 }
監聽也沒開始加載事件,獲取初始加載時間;
1 var resources = []; 2 page.onResourceRequested = function (request) { 3 resource = { 4 "startTime": request.time, 5 "url": request.url 6 }; 7 resources[request.id] = resource; 8 };
監聽資源文件請求事件,獲取資源發起請求的時間;
1 page.onResourceReceived = function (response) { 2 if(response.stage == "start") { 3 resources[response.id].size = response.bodySize; 4 } else if(response.stage == "end") { 5 resources[response.id].endTime = response.time; 6 } 7 };
監聽資源文件加載完成事件,獲取加載完成時間;
1 page.onLoadFinished = function () { 2 endTime = new Date(); 3 timeInSeconds = (endTime - startTime) / 1000; 4 console.log("Loading takes " + timeInSeconds + " seconds."); 5 resources.forEach(function (resource) { 6 st = new Date(resource.startTime).getTime(); 7 et = new Date(resource.endTime).getTime(); 8 timeSpent = (et - st) / 1000; 9 console.log(timeSpent + " seconds : " + resource.url); 10 }); 11 phantom.exit(0); 12 };
監聽文檔加載完成事件,記錄完成時間,並打印出所有資源文件的耗時。
上面的on+事件,做了四件事,監聽資源文件請求和加載完成事件,監聽文檔加載開始完成事件,獲取對應的時間,這樣我們就可以使用這些事件去分析這個頁面的性能問題了。
7、抓取頁面
將要訪問的頁面抓取保存為圖片或者PDF文件的格式,這在PhantomJS里非常簡單。我們下面就分別做一個保存圖片和PDF的例子:
保存為圖片:
1 // a phantomjs example, saved as img 2 var page = require('webpage').create(); 3 page.open("http://www.cnblogs.com/front-Thinking/", function(status) { 4 if ( status === "success" ) { 5 console.log(page.title); 6 page.render("front-Thinking.png"); 7 } else { 8 console.log("Page failed to load."); 9 } 10 phantom.exit(0); 11 });
注:render獲取一個參數,即保存文件的文件名。結果如下:
保存為pdf:
1 // a phantomjs example,saved as pdf file 2 var page = require('webpage').create(); 3 page.open("http://www.baidu.com", function(status) { 4 if ( status === "success" ) { 5 console.log(page.title); 6 page.paperSize = { format: 'A4', 7 orientation: 'portrait', 8 border: '1cm' }; 9 page.render("front-Thinking.pdf"); 10 } else { 11 console.log("Page failed to load."); 12 } 13 phantom.exit(0); 14 });
注:其中,pagerSize設置pdf的格式。結果如下:
利用這些提供的特性,你完全可以做一個爬蟲去爬去別人的網站。
8、文件操作相關
文件操作在編碼中非常有用,例如你可以將一些配置信息放在文件中,在程序執行的過程中去讀取;你也可以將你程序執行過程中一些有用的信息保存為文件。因此文件I/O非常有用。我們舉個簡單的例子,讀取文件信息:
1 var filePath = '/workspace/file1.js';//文件路徑 2 3 //判斷文件是否存在,是文件還是文件夾 4 if( fs.exists(filePath) && fs.isFile(filePath) ) { 5 var ins = fs.open(filePath, 'r');//打開文件 6 while(!ins.atEnd()) {//循環讀取文件內容 7 var buffer = ins.readLine();//一行行的讀取 8 console.log(buffer); 9 } 10 }
這里,讀取文件內容並逐行打印。文件操作有以下幾種方式:
1 r //讀取文件 2 w //寫文件,回覆蓋 3 a //寫文件,追加 4 rb //讀取二進制流 5 rw //寫入二進制流
9、模塊化
模塊化是沒個技術都涉及到的內容,這里不做詳細介紹了。具體可參考阮一峰前輩的博客:http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
10、與jQuery等第三方的結合
有很多第三方非常優秀的庫函數,那么這里我們就舉一個大家都非常喜歡的jQuery庫函數來講講phantomJS與庫函數的結合。代碼如下:
1 var page = require('webpage').create(); 2 page.open("http://www.cnblogs.com/front-Thinking/", function(status) { 3 if ( status === "success" ) { 4 page.render("before.png"); 5 page.includeJs("http://code.jquery.com/jquery-1.10.1.min.js", 6 function() { 7 page.evaluate(function() { 8 $('#Header1_HeaderTitle').html('My PhantomJS'); 9 }); 10 page.render("after.png"); 11 phantom.exit();(); 12 }); 13 } 14 });
以上代碼,訪問我的博客地址,並抓取截屏,加載jquery后修改我博客的標題,結果如下:
before.png
after.png
11、其它
PhantomJS可以做的事情太多了以至於我可能都介紹了只有它N分之一,N趨於無窮大。說了僅僅是入門的帖子,所以就不再深入介紹下去了,當然我也只是個小白,暫時知道了解的也比較淺顯。其實,PhantomJS可以結合Jasmine來一起做測試,可以省去很大的人力和時間成本。同時,開源社區有很多基於PhantomJS做的工具和應用,例如前端爬蟲等,有興趣的不妨去讀讀。
結束語
再次對你有如此大的耐性讀這么一篇爛文到現在表示感謝(花了兩個多小時寫它,我也是夠有耐性的了,哇咔咔)!在閱讀或者敲代碼過程中發現任何問題或者有任何疑問,歡迎留言指導交流!