原文鏈接:http://www.mostclan.com/post-329.html
作者:Veris
Blog:最族 [ http://www.mostclan.com ]
為公司某業(yè)務(wù)實(shí)現(xiàn)“服務(wù)端對(duì)網(wǎng)站截圖”功能髓迎,搜羅了很多技術(shù)最終采用了PhantomJS無頭瀏覽器技術(shù)。
什么是PhantomJS票摇?
PhantomJS是一個(gè)基于webkit的javaScript API略就。它使用QtWebKit作為它核心瀏覽器的功能,使用webkit來編譯解釋執(zhí)行javaScript代碼。任何你可以基于在webkit瀏覽器做的事情,它都能做到联逻。它不僅是個(gè)隱性的瀏覽器,提供了諸如css選擇器检痰、支持wen標(biāo)準(zhǔn)包归、DOM操作、json铅歼、HTML5等公壤,同時(shí)也提供了處理文件I/O的操作,從而使你可以向操作系統(tǒng)讀寫文件等椎椰。phantomJS的用處可謂非常廣泛諸如網(wǎng)絡(luò)監(jiān)測(cè)境钟、網(wǎng)頁截屏、無需瀏覽器的wen測(cè)試俭识、頁面訪問自動(dòng)化等。
網(wǎng)上流傳PhantomJS已暫停維護(hù)洞渔,轉(zhuǎn)而投入selenium的開發(fā)套媚,所以用戶更傾向于目前流行的瀏覽器自動(dòng)化測(cè)試框架“selenium”,這里作者選擇使用PhantomJS來做服務(wù)磁椒,因?yàn)椴渴鸨容^簡(jiǎn)單堤瘤,且完全能滿足業(yè)務(wù)需求。
1浆熔、實(shí)現(xiàn)截圖
var webpage = require('webpage');
var page = webpage.create();
page.settings.javascriptEnabled = false; // 禁用JavaScript代碼
page.open("http://www.mostclan.com", function (status) {
if (status === "success") {
console.log(page.title);
console.log("截圖成功");
page.render("screenshot.png");
} else {
console.log("截圖失敗");
}
});
將文件保存為webpage.js本辐,使用如下命令執(zhí)行程序
phantomjs webpage.js
(這里需要安裝PhantomJS,詳細(xì)步驟不做闡述医增,請(qǐng)自行百度)
如果返回截圖成功慎皱,那么恭喜你截圖服務(wù)已經(jīng)寫好了,就是這么簡(jiǎn)單叶骨,你可以查看目錄下的“screenshot.png”有一張我的博客截圖_茫多。
webpage 是 PhantomJS 的核心模塊,上面的代碼中忽刽,open() 方法有兩個(gè)參數(shù)天揖。
第一個(gè)參數(shù)是請(qǐng)求地址(不要忘記協(xié)議頭),默認(rèn)使用 GET 方式
第二個(gè)參數(shù)是回調(diào)函數(shù)跪帝,回調(diào)參數(shù)status表示網(wǎng)頁狀態(tài)主要有success和fail兩種今膊。
值的注意的是,只要請(qǐng)求有返回結(jié)果伞剑,status參數(shù)就是success斑唬,即使服務(wù)器返回的狀態(tài)是404或500錯(cuò)誤。
如果需要使用POST請(qǐng)求或其他請(qǐng)求要求,可以使用如下方式:
var webpage = require('webpage');
var page = webpage.create();
var settings = {
operation: "POST",
encoding: "utf8",
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify({
params: "data",
array: ["1", "2"]
})
};
page.settings.javascriptEnabled = false; // 禁用JavaScript代碼
page.open("http://www.mostclan.com", settings, function (status) {
if (status === "success") {
console.log(page.title);
console.log("截圖成功");
page.render("screenshot.png");
} else {
console.log("截圖失敗");
}
});
這里第二個(gè)參數(shù)變成一個(gè)對(duì)象類型的配置信息赖钞,可以配置請(qǐng)求方式腰素、編碼、頭信息雪营、數(shù)據(jù)等弓千。
另外代碼中有一句 page.settings.javascriptEnabled = false;
,如果不需要頁面渲染時(shí)執(zhí)行JS代碼献起,可以禁用此選項(xiàng)來加速截圖洋访,因?yàn)橐话憔W(wǎng)站都是由HTML+CSS來渲染頁面的,其他基本上對(duì)渲染結(jié)果影響不大谴餐。
page.render()
可以將打開的網(wǎng)頁截圖并保存成本地圖片姻政,可以將指定的圖片文件名作為參數(shù)傳入,render 方法可以根據(jù)文件名的后綴將圖片保存成對(duì)應(yīng)的格式岂嗓。
目前支持PNG汁展、GIF、JPEG厌殉、PDF四種圖片格式食绿。
page.render('temp.jpeg', {format: 'jpeg', quality: '100'});
其他參數(shù)詳細(xì)細(xì)節(jié)可參考官方文檔
2、搭建WebServer
這里用到一個(gè)基于mongoose的WebServer模塊公罕,因?yàn)樵撃K目前僅允許10個(gè)并發(fā)請(qǐng)求器紧,所以在大流量并發(fā)環(huán)境下請(qǐng)選擇其他Web服務(wù)模塊,或并發(fā)量不高的話可以做個(gè)多服務(wù)的負(fù)載集群楼眷,后文會(huì)介紹操作方法铲汪。
var webserver = require('webserver').create();
webserver.listen(8080, function (request, response) {
response.statusCode = 200;
response.write('<html><h1>Hello World!</h1></html>');
response.close();
});
將文件保存為webserver.js,使用如下命令執(zhí)行程序
phantomjs webserver.js
然后訪問 http://127.0.0.1:8080 便可看到剛剛搭建的Web服務(wù)
listen方法的第一個(gè)參數(shù)可以為一個(gè)端口號(hào)罐柳,也可以是ip:port
這種形式
第二個(gè)參數(shù)是回調(diào)方法掌腰,主要有兩個(gè)回調(diào)參數(shù):request
和response
。
request參數(shù)的幾個(gè)常用屬性:
-
method
硝清,請(qǐng)求方式(get辅斟、post等) -
url
,請(qǐng)求的URL芦拿,包含請(qǐng)求的get參數(shù) -
post
士飒,POST請(qǐng)求數(shù)據(jù) -
postRaw
,POST數(shù)據(jù)原始信息蔗崎,就是application/x-www-form-urlencoded編碼提交的數(shù)據(jù) -
headers
酵幕,請(qǐng)求頭信息
response參數(shù)的幾個(gè)常用方法:
-
statusCode(code)
,設(shè)置HTTP狀態(tài)碼 -
write(data)
缓苛,向response中寫入數(shù)據(jù) -
close()
芳撒,關(guān)閉HTTP連接(這個(gè)比較重要邓深,如果未關(guān)閉會(huì)一直占用連接,后面會(huì)講這個(gè)坑)
3笔刹、API服務(wù)網(wǎng)關(guān)設(shè)計(jì)
Web服務(wù)搭建好后芥备,我們就可以做服務(wù)網(wǎng)關(guān)了,這樣通過調(diào)用網(wǎng)關(guān)便能獲取網(wǎng)站截圖舌菜。
首先規(guī)定交互協(xié)議和返回?cái)?shù)據(jù)格式萌壳,我們協(xié)定如下:
請(qǐng)求參數(shù):(POST請(qǐng)求服務(wù)網(wǎng)關(guān))
- url: 需要截圖的網(wǎng)站地址
- out_order_id: 外部訂單ID,如果是異步回調(diào)通知形式日月,可以加這個(gè)參數(shù)來識(shí)別圖片
- method: 請(qǐng)求方式
- headers: 請(qǐng)求頭
- data: 請(qǐng)求參數(shù)(json格式)
- screen_width: 截圖屏幕寬度(如果有要求的話)
- screen_height: 截圖屏幕高度(如果有要求的話)
- sign: 簽名(這里如果要暴露服務(wù)給外網(wǎng)使用袱瓮,又想限定用戶的話可以設(shè)置簽名參數(shù)來授權(quán),詳細(xì)設(shè)計(jì)方法不再闡述)
響應(yīng)參數(shù):
- code: 狀態(tài)值 0-失敗 1-成功
- msg: 回饋消息
- data: 響應(yīng)數(shù)據(jù)
- screen_image_data: 截屏圖片數(shù)據(jù)(base64加密)
- out_order_id: 外部訂單ID
- timestamp: 時(shí)間戳
設(shè)計(jì)好數(shù)據(jù)格式就可以運(yùn)行服務(wù)了爱咬,完整代碼我分享在github上(見文末)尺借,可運(yùn)行src/server.js
我這在本地使用postman請(qǐng)求服務(wù)網(wǎng)關(guān),可以看到時(shí)間響應(yīng)還是挺快的精拟,部署到服務(wù)器會(huì)更快點(diǎn)燎斩。
渲染效果如下:(我這里圖片采用了jpg壓縮了,追求質(zhì)量可以改成png蜂绎,不過響應(yīng)渲染的速度會(huì)下降一些)
4瘫里、服務(wù)化部署、異常情況及解決方案
高可用問題:為了使服務(wù)正车茨耄可控,一定要使用try()catch(e){}形式捕獲異常局装!部署到Linux需要注意的是——如何在后臺(tái)保持穩(wěn)定運(yùn)行坛吁,這里推薦大家使用Supervisor來守護(hù)進(jìn)程,可以在后臺(tái)運(yùn)行的同時(shí)記錄日志铐尚,當(dāng)進(jìn)程異常奔潰還可以自動(dòng)重載服務(wù)拨脉。(詳情可自行查閱資料)
超時(shí)問題:因?yàn)榻貓D比較耗時(shí),可以改為異步的形式宣增,即服務(wù)處理完成后回調(diào)通知結(jié)果玫膀,圖片則可使用本地存儲(chǔ)/OSS存儲(chǔ)圖片數(shù)據(jù),返回圖片鏈接形式加快響應(yīng)速度爹脾。
字體問題:如果部署在linux上帖旨,很容易出現(xiàn)中文亂碼或顯示不出的問題,這里需要在系統(tǒng)上安裝字體灵妨,可參考https://blog.csdn.net/weiguang1017/article/details/80229133
另外請(qǐng)求服務(wù)的生命周期也至關(guān)重要:
我們可以試著將response.close();
注釋掉解阅,然后啟動(dòng)服務(wù)再請(qǐng)求,會(huì)發(fā)現(xiàn)一直在等待響應(yīng)中泌霍,當(dāng)我如此反復(fù)請(qǐng)求10次發(fā)現(xiàn)無法再請(qǐng)求了
這是因?yàn)?strong>WebServer模塊只支持10個(gè)并發(fā)货抄,而且可以response完沒有close連接,就導(dǎo)致一直占用tcp連接,造成服務(wù)不可用蟹地,我們可以通過linux命令來查看情況
ps aux | grep server.js
拿到進(jìn)程的PID后使用lsof
命令查看進(jìn)程詳情
lsof -nPp 39207
情況如下圖积暖,可以看到有10個(gè)TCP連接,其中有幾個(gè)是CLOSE_WAIT狀態(tài)怪与。
出現(xiàn)大量close_wait
的現(xiàn)象夺刑,主要原因是某種情況下對(duì)方關(guān)閉了socket鏈接(我這里postman取消了請(qǐng)求),但是服務(wù)方處于忙線狀態(tài)琼梆,沒有關(guān)閉連接性誉。
所以response.close();
這一句千萬不能漏,無論是請(qǐng)求成功還是異常情況都要正常響應(yīng)用戶并關(guān)閉連接茎杂。
5错览、服務(wù)擴(kuò)展與展望
如若想要實(shí)現(xiàn)高并發(fā)大流量的服務(wù),則需增加服務(wù)網(wǎng)關(guān)(可分布式部署)煌往,使用nginx反向代理來負(fù)載均衡(反向到各個(gè)服務(wù)網(wǎng)關(guān)的WebServer地址)倾哺,這里和一般網(wǎng)站的高并發(fā)架構(gòu)方式類似,設(shè)計(jì)如下圖:
如果服務(wù)網(wǎng)關(guān)數(shù)量不夠刽脖,扛不住大量并發(fā)訪問羞海,則可以通過限流的形式緩解壓力
至此一個(gè)簡(jiǎn)單的網(wǎng)站截圖服務(wù)就做好了,以如今的設(shè)計(jì)應(yīng)該能滿足日常需要曲管,有更好設(shè)計(jì)方案或想法歡迎留言和issue却邓。
本項(xiàng)目源代碼公開在 https://github.com/VerisFung/WebScreenshotService ,歡迎Star院水!
轉(zhuǎn)載請(qǐng)注明出處:
作者:Veris
最族 [ http://www.mostclan.com ]