概述
這是一個跨度很廣的小記哦,使用node作爲腳本,涵蓋了三個工具:PhantomJS、CasperJS、SpookyJS。目前網上相關的資料比較少,請關注亂燉,我會斷斷續續更新。
那這三個工具有什麼用呢,網上比較專業的說法是:“前端自動化測試工具”,通俗點來說就是一個“沒有UI界面的終端瀏覽器”;如果完整的來看,他應該是NSCP(我個人寫的簡稱,即:node + SpookyJS + CasperJS + PhantomJS)
注意哦,NSCP和傳統的CURL不一樣的地方在於:
- CURL是發起一個URL請求,單項的;而NSCP則是一個真正的網頁請求,請求整個網頁及網頁包含的所有資源
- 效率來說,CURL遠遠比NSCP要快的多
- CURL更適合爬數據,NSCP更多是對於“無界的瀏覽網頁”
那這個工具有什麼用呢?舉幾個簡單例子:
- 網頁快照(截圖)
- 前端自動化測試
- 其他…
PhantomJS
OK,說了這麼多,那怎麼了解NSCP呢?在分享之前必須先了解PhantomJS。CasperJS和SpookyJS都是在PhantomJS基礎上拓展的工具。PhantomJS是一個無界的終端瀏覽器,他能夠訪問並測試指定網頁。
安裝方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# Mac brew 安裝 brew install phantomjs
# Npm 安裝 npm install phantomjs
# CentOS 編譯安裝 sudo yum install gcc gcc -c++ make git openssl-devel freetype-devel fontconfig-devel
git clone git: //github .com /ariya/phantomjs .git
cd phantomjs
git checkout 1.9 . /build .sh
|
簡單的例子
將下面保存爲simple.js文件
1
2
3
4
5
6
7
8
9
|
console.log( 'Loading a web page' );
var page = require( 'webpage' ).create();
var url = 'http://levi.cg.am/' ;
page.open(url, function (status) {
// Page is loaded!
phantom.exit();
}); |
終端執行命令
1
|
phantomjs simple.js |
更多示例見:http://phantomjs.org/examples/
有人說PhantomJS是node,這樣理解不是很正確,PhantomJS是一個運行JS調用API接口的工具,允許通過npm方式下載安裝並以模塊方式存在node中。但是PhantomJS中是不支持node方法的哦,具體差異見官方文檔!
同類的軟件
挺多的,這裏我只列舉一個SlimerJS。SlimerJS與PhantomJS不同之處在於,PhantomJS是基於webkit內核的,而SlimerJS是基於Gecko(firefox),SlimerJS和PhantomJS語法很接近,不重複說明,具體見官網
http://www.slimerjs.org/index.html
在PHP中使用PhantomJS
方法一:通過exec
1
2
|
<?php exec ( 'phantomjs simple.js' );
|
方法二:PHP PhantomJS
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?php require '../vendor/autoload.php' ;
use JonnyW\PhantomJs\Client;
$client = Client::getInstance();
$request = $client ->getMessageFactory()->createRequest();
$response = $client ->getMessageFactory()->createResponse();
$request ->setMethod( 'GET' );
$request ->setUrl( 'http://google.com' );
$client ->send( $request , $response );
var_dump( $response );
|
安裝方法及使用見官方git:
https://github.com/jonnnnyw/php-phantomjs
CasperJS
通過上面提供的PhantomJS示例並瞭解後,你可能會發現一些問題,比如你要請求一系列網址,並且在一個請求之後,執行另一個請求。使用PhantomJS可能會像這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var page = require( 'webpage' ).create(); //新建一個頁面
page.open(url1, function (status) { //導航到第一個URL
if (status == "fail" ) phantom.exit(); //如果發生錯誤,退出程序
page.open(url2, function (status) { //否則在頁面加載完成的回調函數中繼續導航到第二個URL,依次類推
if (status == "fail" ) phantom.exit();
page.open(url3, function (status) {
if (status == "fail" ) phantom.exit();
page.open(url4, function (status) {
if (status == "fail" ) phantom.exit();
// 我可以停下來了嗎?
});
});
});
}); |
是不是很恐怖,而CasperJS的目的就在於更方便的操作PhantomJS。CasperJS是一個開源的,用JavaScript編寫的,基於PhantomJS的導航腳本和測試工具,它簡化了定義一個完成的導航操作所需的步驟,還提供了很有用的函數封裝、方法、和語法糖、它可以完成下面這些常見任務:
- 定義 & 排序瀏覽器導航步驟
- 填充 & 提交表單
- 點擊 & 跟蹤鏈接
- 捕獲網頁截圖 (還可以截取某一區域)
- 在遠程DOM上進行斷言測試
- 記錄事件
- 下載資源,包括二進制文件
- 編寫功能測試套件,結果保存爲JUnit XML文件
- 抓取網頁內容
CasperJS使用更方便的API解決了這種異步操作的問題:
1
2
3
4
5
6
7
8
|
var casper = require( 'casper' ).create(); //新建一個頁面
casper.start(url1); //添加第一個URL
casper.thenOpen(url2); //添加第二個URL,依次類推
casper.thenOpen(url3); casper.thenOpen(url4); casper.run(); //開始導航
|
終端執行文件
1
|
casperjs simple.js |
更多示例見官方文檔:
http://casperjs.readthedocs.org/en/latest/index.html
注意哦:CasperJS使用的是casper的方法,PhantonJS使用的是phantom的方法;他們有各自的使用方法,不可以用混淆執行
安裝方法
目前我只在本地開發環境安裝,安裝方法如下:
1
|
brew install casperjs --devel
|
注意哦:目前PhantomJS的版本是1.9.2,正式版是不支持的,請安裝開發版
SpookyJS
通過上面描述,應該瞭解到:
- PhantomJS,一個無界的終端瀏覽器
- CasperJS,一個改善PhantomJS操作的工具
而SpookyJS有什麼用途呢?前面說了PhantonJS和CasperJS是可以作爲模塊進行npm安裝在node中,但是執行的方法卻是通過各自的bin文件,諸如:
1
2
|
phantomjs simple.js casperjs simple.js |
而SpookyJS則是一個驅動器,在node下能夠正常使用PhantonJS和CasperJS,這就是我定義的NSCP (node + SpookyJS + CasperJS + PhantomJS)。你可以簡單將其理解爲:
- node:腳本語言
- Spooky:驅動器
- CasperJS:工具
- PhantomJS:無界瀏覽器
安裝方法
既然SpookyJS作爲node的驅動器,那麼也需要通過node進行安裝:
1
|
npm install spooky
|
SpookyJS需要依賴的環境
簡單示例
保存爲hello.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
try {
var Spooky = require( 'spooky' );
} catch (e) {
var Spooky = require( '../lib/spooky' );
} var spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug' ,
verbose: true
}
}, function (err) {
if (err) {
e = new Error( 'Failed to initialize SpookyJS' );
e.details = err;
throw e;
}
spooky.start(
'http://en.wikipedia.org/wiki/Spooky_the_Tuff_Little_Ghost' );
spooky.then( function () {
this .emit( 'hello' , 'Hello, from ' + this .evaluate( function () {
return document.title;
}));
});
spooky.run();
});
spooky.on( 'error' , function (e, stack) {
console.error(e);
if (stack) {
console.log(stack);
}
}); /* // Uncomment this block to see all of the things Casper has to say. // There are a lot. // He has opinions. spooky.on('console', function (line) { console.log(line);
}); */ spooky.on( 'hello' , function (greeting) {
console.log(greeting);
}); spooky.on( 'log' , function (log) {
if (log.space === 'remote' ) {
console.log(log.message.replace(/ \- .*/, '' ));
}
}); |
通過node執行腳本
1
|
node hello.js |
更多內容請見官方git:
需要注意的幾個地方
通過SpookyJS配置PhantomJS和CasperJS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug' ,
verbose: true ,
viewportSize: {
width: 1440, height: 768
},
pageSettings: {
outputEncoding: 'gbk'
}
}
}, function (err) {});
|
phantom和casper不同的方法調用
1
2
3
4
5
6
7
8
9
10
11
12
|
spooky.start( 'http://example.com/the-page.html' );
spooky.then( function () {
// 這裏是CasperJS的方法
}); spooky.thenEvaluate( function () {
// 這裏是PhantomJS的方法
}) // this function (and the three spooky calls above) runs in Spooky's environment spooky.run(); |
變量作用域
SpookyJS的作用域和js不一樣哦,具體看下面幾個來自官方的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var x = 'spooky' ;
spooky.start( 'http://example.com/the-page.html' );
spooky.then( function () {
var y = 'casper' ;
console.log( 'x:' , x); // -> x: undefined
}); spooky.thenEvaluate( function () {
console.log( 'x:' , x); // -> x: undefined
console.log( 'y:' , y); // -> y: undefined
}); spooky.run(); |
而PhantomJS的方法中可以通過傳遞一個對象過去
1
2
3
4
5
6
7
8
|
var x = 'spooky' ;
// spooky.thenEvaluate accepts an options argument (just like Casper) spooky.thenEvaluate( function (x) {
console.log( 'x:' , x); // -> x: spooky
}, { x: x
}); |
而CasperJS的方法中需要將變量作爲數組對象,以參數傳過去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var x = 'spooky' ;
var y = 'kooky' ;
// spooky.then accepts a function tuple spooky.then([{ x: x
}, function () {
console.log( 'x:' , x); // -> x: spooky
}]); // spooky.thenEvaluate accepts both a function tuple and an argument hash spooky.thenEvaluate([{ y: y
}, function (x) {
console.log( 'x:' , x); // -> x: spooky
console.log( 'y:' , y); // -> y: kooky
}], { x: x
}); |
全局和局部變量
1
2
3
4
5
6
7
8
9
10
|
var x = 'spooky' ;
spooky.then([{ x: x
}, function () {
x = 'casper' ;
console.log( 'x:' , x); // -> x: casper
}]); console.log( 'x:' , x); // -> x: spooky
|
一個混合的使用例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
spooky.start( 'http://example.com/the-page.html' );
var x = 'spooky' ;
spooky.then([{ x: x
}, function () {
var y = 'casper' ;
var z = this .evaluate( function (x, y) {
console.log( 'x:' , x); // -> x: spooky
console.log( 'y:' , y); // -> y: casper
return [x, y, 'and the-page.html' ].join( ', ' );
}, {
x: x,
y: y
});
console.log( 'z:' , z); // -> z: spooky, casper, and the-page.html
}]); spooky.run(); |
那兩個不同的方法之間,如何傳遞變量呢?來一個非官方的例子,同樣來自github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
spooky.then( function () {
window.pageCount = 0;
}); spooky.waitFor( function () {
var isLastPage = ! this .exists( '#next' );
if (!isLastPage) {
window.pageCount++;
this .click( '#next' );
}
return isLastPage;
}); spooky.then( function () {
this .emit( 'console' , 'Page count: ' + window.pageCount);
}); |
是不是有點不太好理解,如果你瞭解PHP的話,可以將其當作PHP閉包中的ues去看待。如果仍舊不懂,你可以給我留言哦
不允許使用中文
這個真的是官方、非官方都沒有說明的,無論你是不是utf-8,在SpookyJS構造方法中都不允許使用中文
1
2
3
4
5
6
7
8
9
10
11
12
|
var spooky = new Spooky({
// 此處省略...
}, function () {
spooky.then( function () {
// 這樣是不可以的哦
var name = '中文' ;
}
}); spooky.on( 'test' , function (log) {
console.log( '但是這裏是可以用中文的哦!' );
}); |
好在JS中有一個原生的方法 decodeURIComponent,可以這樣
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var spooky = new Spooky({
// 此處省略...
}, function () {
spooky.then( function () {
var name = this .evaluate( function () {
return window.decodeURIComponent( '%E4%B8%AD%E6%96%87' );
});
}
}); spooky.on( 'test' , function (log) {
console.log( '但是這裏是可以用中文的哦!' );
}); |
當然也可以使用base64,目前還沒測。原因,我找了很多資料,唯一有個相關的說法是CasperJS不支持大於8b的字符
轉載自:http://levi.cg.am/archives/3648