[TOC]
0x00 前言初识 1.PhantomJS 介绍 什么是PhantomJS?
Phantomjs(/ˈfæntəm/js
) 是一个基于WebKit库的无头(没有显示界面)的JavaScript API,即像在web浏览器上运行一样,所以标准的DOM脚本和CSS选择器工作正常,用于自动化Web浏览器操作,是一个免费开源的轻量级服务器解决方案。 它可以在Windows、macOS、Linux和FreeBSD上运行, 并且使用QtWebKit作为后端,它为各种web标准提供了快速的本地支持:DOM处理、CSS选择器、JSON、画布和SVG。
PhantomJS有什么用?
它可以用来测试动态内容, 比如 AJAX内容、截屏,以及转换为PDF和原型图,它也可以执行跨浏览器的JavaScript测试,可以模拟网络延迟、网页截屏、页面访问自动化以及捕获网络脚本的错误和警告等。 它不仅是个隐形的浏览器, 还提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,同时也提供了处理文件I/O的操作,从而使你可以向操作系统读写文件等。 简单的说, PhantomJS 适合执行各种页面自动化监控、测试任务等。
2.参考来源
0x01 PhantomJS 安装 描述: 我们知道 PhantomJS
它可以在 Windows、macOS、Linux和FreeBSD
上运行,你可以参考官网中安装说明进行快速安装 (PS: 软件下载地址 https://phantomjs.org/download.html )。
下面会根据使用场景,从最常用的Windows 以及 Linux 系统发行版本里安装PhantomJS流程进行简单说明:
1.Windows 描述:首先我们需要下载 Windows 版本的 PhantomJS 压缩包 , 选择 Windows 运行的版本进行下载然后放在一个指定目录中,例如此处的F:\WeiyiGeek\Tools
(建议加上环境变量);
[TOC]
0x00 前言初识 1.PhantomJS 介绍 什么是PhantomJS?
Phantomjs(/ˈfæntəm/js
) 是一个基于WebKit库的无头(没有显示界面)的JavaScript API,即像在web浏览器上运行一样,所以标准的DOM脚本和CSS选择器工作正常,用于自动化Web浏览器操作,是一个免费开源的轻量级服务器解决方案。 它可以在Windows、macOS、Linux和FreeBSD上运行, 并且使用QtWebKit作为后端,它为各种web标准提供了快速的本地支持:DOM处理、CSS选择器、JSON、画布和SVG。
PhantomJS有什么用?
它可以用来测试动态内容, 比如 AJAX内容、截屏,以及转换为PDF和原型图,它也可以执行跨浏览器的JavaScript测试,可以模拟网络延迟、网页截屏、页面访问自动化以及捕获网络脚本的错误和警告等。 它不仅是个隐形的浏览器, 还提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,同时也提供了处理文件I/O的操作,从而使你可以向操作系统读写文件等。 简单的说, PhantomJS 适合执行各种页面自动化监控、测试任务等。
2.参考来源
0x01 PhantomJS 安装 描述: 我们知道 PhantomJS
它可以在 Windows、macOS、Linux和FreeBSD
上运行,你可以参考官网中安装说明进行快速安装 (PS: 软件下载地址 https://phantomjs.org/download.html )。
下面会根据使用场景,从最常用的Windows 以及 Linux 系统发行版本里安装PhantomJS流程进行简单说明:
1.Windows 描述:首先我们需要下载 Windows 版本的 PhantomJS 压缩包 , 选择 Windows 运行的版本进行下载然后放在一个指定目录中,例如此处的F:\WeiyiGeek\Tools
(建议加上环境变量);
1 2 3 4 5 6 7 8 9 10 11 12 13 $InstallPath ="F:\WeiyiGeek\Tools" mkdir $InstallPath $InstallUrl ="https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-windows.zip" $down =New-Object "System.Net.WebClient" $down .DownloadFile($InstallUrl ,"${InstallPath}\phantomjs-2.1.1-windows.zip" )Expand-Archive -Path "${InstallPath}/phantomjs-2.1.1-windows.zip" -DestinationPath $InstallPath -Force $systempath = [System.Environment]::GetEnvironmentVariable("PATH" ,"Machine" )$systempath = $systempath + ";" + $InstallPath + "\phantomjs-2.1.1-windows\bin" [System.Environment]::setEnvironmentVariable("PATH" ,$systempath ,"Machine" )
验证安装:1 2 3 4 5 6 7 8 9 10 11 12 13 phantomjs-2.1.1-windows\bin>phantomjs.exe -v 2.1.1 phantomjs phantomjs> phantom.version { "major" : 2, "minor" : 1, "patch" : 1 } phantomjs> var system = require('system' )
2.Linux 此处演示在 CentOS7/Ubuntu x64位 (Linux 64-bit)系统中安装 phantomjs 流程,若是其他发行版也可参照安装。
温馨提示: It however still relies on Fontconfig (the package fontconfig or libfontconfig, depending on the distribution). The system must have GLIBCXX_3.4.9 and GLIBC_2.7.
安装脚本: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 apt install jq bzip2 || yum install jq bzip2 if [ -f /etc/redhat-release ]; then yum install fontconfig freetype2 -y yum install bitmap-fonts bitmap-fonts-cjk -y yum groupinstall "fonts" -y fc -cache else sudo apt update sudo apt install -y fontconfig libfreetype6 libfreetype6-dev mkfontscale sudo apt-get install build-essential chrpath libssl-dev libxft-dev mkdir /usr/share/fonts/Windows-Fonts chmod -R 644 /usr/share/fonts/Windows-Fonts && cd /usr/share/fonts/Windows-Fonts mkfontscale;mkfontdir;fc -cache ln -s /usr/share/fonts/Windows-Fonts /usr/lib/x86_64-linux-gnu/fonts fi cd /tmpwget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 tar -jxvf phantomjs-2.1.1-linux-x86_64.tar.bz2 mv phantomjs-2.1.1-linux-x86_64/ /usr/local /src/phantomjs ln -sf /usr/local /src/phantomjs/bin/phantomjs /usr/local /bin/phantomjs phantomjs -v phantomjs /usr/local /src/phantomjs/examples/hello.js
0x02 快速使用 1.牛刀小试 下面来看一个简单示例, 它是参考整合 PhantomJS 官网 的演示代码,实现终端输出、命令行参数获取、请求指定url获取站点Dom相关信息以及站点任何控制台消息,与输出站点首页截图并保存为域名.png图片。
温馨提示: 下述演示代码都能在 https://github.com/WeiyiGeek/SecOpsDev/tree/master/AutomatedTesting/Web/phantomjs 找到
示例代码: 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 console .log('Hello, world! PhantomJS Demo!' );var page = require ('webpage' ).create(),system = require ('system' ), t, url; if (system.args.length === 1 ) { console .log('Usage: hello-PhantomJS.js [some URL]' ); phantom.exit(); } t = Date .now(); url = system.args[1 ]; page.onConsoleMessage = function (msg ) { console .log('Console output : ' + msg); }; page.open(url, function (status ) { if (status !== 'success' ) { console .log('FAIL to load the address : ' + url); } else { t = Date .now() - t; var title = page.evaluate(function ( ) { return document .title; }); console .log("----------------------------------------------" ) console .log("Status: " + status); console .log('Loading ' + url + ', Title ' + title ); console .log('Loading time ' + t + ' msec' ); page.render(url.split("//" )[1 ]+'.png' ); } phantom.exit(); });
执行结果:1 2 3 4 5 6 7 8 9 10 11 12 phantomjs-2.1 .1 -windows\bin> CHCP 65001 phantomjs-2.1 .1 -windows\bin> .\phantomjs.exe .\hello-PhantomJS.js https: Hello, world! PhantomJS Demo! Console output : 电脑 Console output : Welcome to WeiyiGeek'Index Site - [https://www.weiyigeek.top]. 花开堪折直须折,莫待无花空折枝! Console output : %c希望与各位志同道合的朋友一起学习交流,如文章有误请留下您宝贵的知识建议,或者通过邮箱【master#weiyigeek.top】联系我哟! color:white Console output : %c专栏书写不易,如果您觉得这个专栏还不错的,请给这篇专栏 【点个赞、投个币、收个藏、关个注,转个发】(人间五大情),这将对我的肯定,谢谢支持!(๑′ᴗ‵๑) ❤! color:red ---------------------------------------------- Status: success Loading https://weiyigeek.top, Title 🌐 WeiyiGeek|唯一极客-Geek-IT网络安全技术知识分享-主页站点 Loading time 684 msec
weiyigeek.top-phantomjs-牛刀小试图
扩展了解 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 var page = require('webpage' ).create(); page.childFramesCount page.childFramesName page.close page.currentFrameName page.deleteLater page.destroyed page.evaluate page.initialized page.injectJs page.javaScriptAlertSent page.javaScriptConsoleMessageSent page.loadFinished page.loadStarted page.openUrl page.release page.render page.resourceError page.resourceReceived page.resourceRequested page.uploadFile page.sendEvent page.setContent page.switchToChildFrame page.switchToMainFrame page.switchToParentFrame page.addCookie page.deleteCookie page.clearCookies onInitialized onLoadStarted onLoadFinished onUrlChanged onNavigationRequested onRepaintRequested onResourceRequested onResourceReceived onResourceError onResourceTimeout onAlert onConsoleMessage onClosing
参考地址: https://phantomjs.org/api/webpage/
温馨提示: 更多的PhantomJS示例尽在 phantomjs-2.1.1-windows\examples
目录之中,文件说明可参考官方文档 (https://phantomjs.org/examples ),大家可以在开发时多多参考。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 arguments.js shows the arguments passed to the script countdown.js prints a 10 second countdown echoToFile.js writes the command line arguments to a file fibo.js lists the first few numbers in the Fibonacci sequence hello.js displays the famous message module.js and universe.js demonstrate the use of module system outputEncoding.js displays a string in various encodings printenv.js displays the system’s environment variables scandir.js lists all files in a directory and its subdirectories sleepsort.js sorts integers and delays display depending on their values version.js prints out PhantomJS version number page_events.js prints out page events firing: useful to better grasp page.on* callbacks colorwheel.js creates a color wheel using HTML5 canvas rasterize.js rasterizes a web page to image or PDF render_multi_url.js renders multiple web pages to images injectme.js injects itself into a web page context phantomwebintro.js uses jQuery to read .version element text from phantomjs.org unrandomize.js modifies a global object at page initialization waitfor.js waits until a test condition is true or a timeout occurs detectsniff.js detects if a web page sniffs the user agent loadspeed.js computes the loading speed of a web site netlog.js dumps all network requests and responses netsniff.js captures network traffic in HAR format post.js sends an HTTP POST request to a test server postserver.js starts a web server and sends an HTTP POST request to it server.js starts a web server and sends an HTTP GET request to it serverkeepalive.js starts a web server which answers in plain text simpleserver.js starts a web server which answers in HTML Testing run-jasmine.js runs Jasmine based tests run-qunit.js runs QUnit based tests Browser features.js detects browser features using modernizr.js useragent.js changes the browser’s user agent property
2.DOM 操作并获取元素属性 描述: 使用标准DOM API或jQuery等常用库访问网页并提取信息。
示例代码
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 58 59 var page = require ('webpage' ).create(),system = require ('system' ), url,textContent; if (system.args.length === 1 ) { console .log('Usage: hello-PhantomJS.js [some URL]' ); phantom.exit(); } else { url = system.args[1 ]; } console .log('The default user agent is ' + page.settings.userAgent);page.settings.userAgent = 'WeiyiGeekAgent' ; page.open(url, function (status ) { console .log("----------------- 分隔线 -------------------------" ) console .log("Status: " + status); if (status !== 'success' ) { console .log('Unable to access site : ' + url); } else { setTimeout(function ( ) { textContent = page.evaluate(function ( ) { return document .getElementsByClassName("post-card-title" )[0 ].textContent ; }); console .log('textContent :' + textContent ); page.render(url.split("//" )[1 ]+'.png' ); phantom.exit(); }, 2000 ); console .log('page Title : ' + page.title); console .log('page Url : ' + page.url); console .log('page Cookies :' + page.cookies[1 ].name + " : " + page.cookies[1 ].value); console .log('page ZoomFactor : ' + page.zoomFactor); console .log('page OfflineStoragePath : ' + page.offlineStoragePath); console .log('page LibraryPath : ' + page.libraryPath); page.includeJs("https://blog.weiyigeek.top/js/jquery/2.1.0-jquery.min.js?v=1.6.6" , function ( ) { var Title = page.evaluate(function ( ) { $("a" )[10 ].click(); return document .title; }); console .log("Blog Title : " + Title); }); } });
执行结果:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 phantomjs-2.1.1-windows\bin>phantomjs.exe 1.page-automation.js https://weiyigeek.top The default user agent is Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1 ----------------- 分隔线 ------------------------- Status: success page Title : 🌐 WeiyiGeek|唯一极客-Geek-IT网络安全技术知识分享-主页站点 page Url : https://weiyigeek.top/ page Cookies :Hm_lvt_8bb888f4c802ff4cd2fdbc10d8ab7069 : 1673344341,1673344489,1673344525,1673344554 page ZoomFactor : 1 page OfflineStoragePath : C:/Users/WeiyiGeek/AppData/Local/Ofi Labs/PhantomJS page LibraryPath : L:/DevOps/自动化测试/模拟浏览器访问/PhantomJS/phantomjs-2.1.1-windows/bin Blog Title : 🌐 WeiyiGeek|唯一极客-Geek-IT网络安全技术知识分享-主页站点 textContent :唯一极客的学习之路汇总 IP - - [10/Jan/2023:17:56:16 +0800] "GET /2018/1-1-1.html HTTP/1.1" 200 16602 "https://weiyigeek.top/" "WeiyiGeekAgent" "-" rt=0.000 urt=- IP - - [10/Jan/2023:17:56:17 +0800] "GET /search.xml HTTP/1.1" 200 30200 "https://blog.weiyigeek.top/2018/1-1-1.html" "WeiyiGeekAgent" "-" rt=0.000 urt=-
weiyigeek.top-使用进行 phantomjs DOM 操作并获取元素属性图
3.网页屏幕截图 描述: 由于PhantomJS使用的是WebKit,这是一个真正的布局和渲染引擎,它可以将网页捕获为屏幕截图, 因为PhantomJS可以在网页上呈现任何内容,所以它可以用来转换CSS样式的HTML内容,也可以转换SVG、图像和Canvas元素。
实际上在前面的案例中, 我们已经使用 PhantomJS 屏幕截图这一功能, 此处在深入讲解一下导出为pdf格式。
在 examples 子目录,还有一个脚本 rasterize.js
这表明更加完整呈现 PhantomJS 特征, 下述也罗列作者在使用中所遇到过的问题。
如何延迟截图,页面请求的资源,如图片、异步cgi、js等,返回的时间以及执行的长短都是不确定的,如果截图过早,可能很多空白区域,因此需要定时截图,在打开页面后,使用setTimeout来延迟截图1 2 3 4 5 window .setTimeout(function ( ) { page.render("weiyigeek.top.png" ); phantom.exit(); }, 1000 );
如何保证网页站点完整截图。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 page.viewportSize = {width : 1024 ,height : 720 }; page.clipRect = { top: 14 , left: 3 , width: 400 , height: 300 }; page.paperSize = { width: '5in' , height: '7in' , margin: { top: '50px' , left: '20px' } }; window .scrollTo(0 ,10000 );window .document.body.scrollTop = document .body.scrollHeight;
简单示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 phantomjs.exe ..\examples\rasterize.js https://blog.weiyigeek.top blog.weiyigeek.pdf phantomjs.exe ..\examples\render_multi_url.js https://weiyigeek.top https://blog.weiyigeek.top Rendered 'https://weiyigeek.top' at 'rendermulti-1.png' Rendered 'https://blog.weiyigeek.top' at 'rendermulti-2.png' phantomjs.exe ..\examples\responsive-screenshot.js https://blog.weiyigeek.top Saving blog.weiyigeek.top/2023-1-11_09-39-25-297_320.png Saving blog.weiyigeek.top/2023-1-11_09-39-26-934_480.png Saving blog.weiyigeek.top/2023-1-11_09-39-27-816_768.png Saving blog.weiyigeek.top/2023-1-11_09-39-28-852_1024.png Saving blog.weiyigeek.top/2023-1-11_09-39-33-267_1200.png
4.网页站点请求 由于PhantomJS允许检查网络流量,因此它适合于对网络行为和性能进行各种分析,可以使用onResourceRequested和onResourceReceived嗅探所有资源请求和响应。脚本netlog.js中说明了记录每个请求和响应的一个非常简单的示例:1 2 3 4 5 6 7 8 9 var page = require ('webpage' ).create();page.onResourceRequested = function (request ) { console .log('Request ' + JSON .stringify(request, undefined , 4 )); }; page.onResourceReceived = function (response ) { console .log('Receive ' + JSON .stringify(response, undefined , 4 )); }; page.open(url);
简单示例: 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 phantomjs.exe ..\examples\netsniff.js https://www.weiyigeek.top timings: { blocked: 0, dns: -1, connect: -1, send: 0, wait : startReply.time - request.time, receive: endReply.time - startReply.time, ssl: -1 } phantomjs.exe ..\examples\netlog.js https://weiyigeek.top | more requested: { "headers" : [ { "name" : "Accept" , "value" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }, { "name" : "User-Agent" , "value" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1" } ], "id" : 1, "method" : "GET" , "time" : "2023-01-11T01:46:10.579Z" , "url" : "https://weiyigeek.top/" } received: { "body" : "" , "bodySize" : 14489, "contentType" : "text/html; charset=utf-8" , "headers" : [ { "name" : "Server" , "value" : "nginx" }, .... }
导入.示例1生成的HAR到可视化viewer显示(http://www.softwareishard.com/har/viewer/ ):
5.简单的Web服务 使用 PhantomJS 我们很容易实现一个 Web Server, 下面我们来实践看看。
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 "use strict" ;var port, server, service, system = require ('system' ); if (system.args.length !== 2 ) { console .log('Usage: simpleserver.js <portnumber>' ); phantom.exit(1 ); } else { port = system.args[1 ]; server = require ('webserver' ).create(); service = server.listen(port, function (request, response ) { console .log('Request at ' + new Date ()); console .log(JSON .stringify(request, null , 4 )); response.statusCode = 200 ; response.headers = { 'Cache' : 'no-cache' , 'Content-Type' : 'text/html' }; response.write('<html>' ); response.write('<head>' ); response.write('<title>Hello, world!</title>' ); response.write('</head>' ); response.write('<body>' ); response.write('<p>This is from PhantomJS web server.</p>' ); response.write('<p>Request data:</p>' ); response.write('<pre>' + JSON .stringify(request, null , 4 )+'</pre>' ); response.write('<p>response data:</p>' ); response.write('<pre>' + JSON .stringify(response, null , 4 )+'</pre>' ); response.write('</body>' ); response.write('</html>' ); response.close(); }); if (service) { console .log('Web server running on port ' + port); } else { console .log('Error: Could not create web server listening on port ' + port); phantom.exit(); } var url = "http://localhost:" + port + "/foo/bar.php?asdf=true" ; console .log("SENDING REQUEST TO:" ); console .log(url); page.open(url, function (status ) { if (status !== 'success' ) { console .log('FAIL to load the address' ); } else { console .log("GOT REPLY FROM SERVER:" ); console .log(page.content); } phantom.exit(); }); }
weiyigeek.top-simpleserver图
0x03 项目实践 1.使用Java+phantomjs实现站点截图捕获并将a标签链接标红。 此处,在Windows平台下实践,当然Linux下执行也是没有问题的,你可以需要安装相应的java环境而已。
2.screen-capture.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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 var page = require ('webpage' ).create(),system = require ('system' ), url,outputType,size,nowTime; if ( system.args.length == 1 ){ console .log("Usage: screen-capture.js url [png|pdf] [paperwidth*paperheight|paperformat] [zoom]" ) console .log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"' ); console .log(' image (png/jpg output) examples: "1920px" entire page, window width 1920px' ); console .log(' "800px*600px" window, clipped to 800x600' ); phantom.exit(); }else { url = system.args[1 ]; outputType = system.args[2 ]; nowTime = Date .now(); pageWidth = 1024 ; pageHeight = 720 ; page.viewportSize = { width : pageWidth, height : pageHeight }; if (system.args.length > 3 && outputType === "pdf" ) { size = system.args[3 ].split('*' ); page.paperSize = size.length === 2 ? { width : size[0 ], height : size[1 ], margin : '0px' } : { format : 'A4' , orientation : 'portrait' , margin : '1cm' }; } else if (system.args.length > 3 && system.args[3 ].substr(-2 ) === "px" ) { size = system.args[3 ].split('*' ); if (size.length === 2 ) { pageWidth = parseInt (size[0 ], 10 ); pageHeight = parseInt (size[1 ], 10 ); page.viewportSize = { width : pageWidth, height : pageHeight }; page.clipRect = { top : 0 , left : 0 , width : pageWidth }; } else { console .log("size:" , system.args[3 ]); pageWidth = parseInt (system.args[3 ], 10 ); pageHeight = parseInt (pageWidth * 3 /4 , 10 ); console .log ("pageHeight:" ,pageHeight); page.viewportSize = { width : pageWidth, height : pageHeight }; } } if (system.args.length > 4 ) { page.zoomFactor = system.args[4 ]; } page.open(url, function (status ) { if (status != "success" ){ console .log('FAIL to load the address' ); phantom.exit(); } else { page.evaluate(function ( ) { window .scrollTo(0 ,document .body.scrollHeight); window .setTimeout(function ( ) { var plist = document .querySelectorAll("a" ); var len = plist.length; while (len) { len--; var el = plist[len]; el.style.border = "1px solid red" ; } }, 2000 ); }); window .setTimeout(function ( ) { filename = url.split("//" )[1 ]+"_" +nowTime+"." +outputType; page.render(filename); console .log("Output : " + filename); phantom.exit(); }, 3000 ); }; }); }
命令行执行:1 2 3 4 5 6 7 8 9 10 11 > phantomjs.exe 2.screen-capture.js Usage: screen-capture.js url [png|pdf|jpeg|bmp|ppm|gif] [paperwidth*paperheight|paperformat] [zoom] paper (pdf output) examples: "5in*7.5in" , "10cm*20cm" , "A4" , "Letter" image (png/jpg output) examples: "1920px" entire page, window width 1920px "800px*600px" window, clipped to 800x600 > phantomjs.exe 2.screen-capture.js https://blog.weiyigeek.top/2018/1-1-1.html png 1024px*720px Output : blog.weiyigeek.top/2018/1-1-1.html_1673407992804.png > phantomjs.exe 2.screen-capture.js https://blog.weiyigeek.top/2018/1-1-1.html pdf 1024px*720px Output : blog.weiyigeek.top/2018/1-1-1.html_1673408871418.pdf
weiyigeek.top-生成PDF与PNG图片
此处使用java操作phantomjs的代码示例: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 58 59 60 61 62 63 package top.weiyigeek.weixin; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class DynamicDownLoad { public static String getSrcContent (String url, String type) { String path = "L:/DevOps/自动化测试/模拟浏览器访问/PhantomJS/phantomjs-2.1.1-windows/bin" ; Runtime rt = Runtime.getRuntime(); Process process = null ; try { process = rt.exec(path + "phantomjs.exe L:/example/2.screen-capture.js" + url.trim() + " " + type.trim()); } catch (IOException e) { e.printStackTrace(); } InputStream is = process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); StringBuffer sbf = new StringBuffer(); String tmp = "" ; try { while ((tmp = br.readLine())!=null ){ sbf.append(tmp); } } catch (IOException e) { e.printStackTrace(); } return sbf.toString(); } public static void main (String[] args) { String src = DynamicDownLoad.getSrcContent("https://weiyigeek.top" ,"pdf" ,"1024px*720px" ); System.out.println(src); } }
补充:对于延迟截图,还是有个问题,就是无法监听ajax或者资源是否完整加载导致页面不全;解决方案 viewport设置一个比截图高度的矮,通过比较生产图片的高度来判断截取图片的结果
2.监控网站主页变化并截图到企业微信预警 好,下面来到了我们项目实践了,主要实现的功能是利用Shell脚本以及crontab定时任务以及 PhantomJS 来监控网站首页的变化,并以截图的方式通知给企业微信对应运维群,及时了解网站运行安全,防止网站主页被黑、被劫持的风险。
此处我是在CentOS7中实现的,安装方法请参考前面章节。
项目地址: https://github.com/WeiyiGeek/SecOpsDev/tree/master/AutomatedTesting/Web/phantomjs/Project/Shell
(PS: 文章中示例代码可能随着时间推移会有更新,建议小伙伴通过上面👆Github地址获取哟!)
项目脚本与PhantomJS脚本文件 1 2 3 4 5 6 /GitProject/SecOpsDev1/AutomatedTesting/Web/phantomjs/Project/Shell 1.WebScreenCapture.sh screen-capture.js mkdir /usr/local /src/phantomjs/custom cp screen-capture.js /usr/local /src/phantomjs/custom
Shell 脚本 WebMonitorScreenCapture.sh 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 #!/bin/bash MONITORSITE=/tmp/target.txt cat > ${MONITORSITE} <<'EOF' https://www.weiyigeek.top https://blog.weiyigeek.top EOF export WXMSGURL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=51648169-7638-43a4-97d6-dfd39b48ea23" export NETTYPE="外网访问" export HCMSG="" export XKMSG="" export ZHCXMSG="" function DependencyCheck (){ phantomjs -v > /dev/null 2>&1 if [[ "$?" != "0" ]];then echo -e "\e[31m[Error] Phantomjs NotFound,Please Install this! \e[0m" ;exit 0;fi jq --version > /dev/null 2>&1 if [[ "$?" != "0" ]];then echo -e "\e[31m[Error] Phantomjs jq,Please Install jq! \n$ yum install jq \e[0m" ;exit 0;fi } function SendWXMsg (){ if [[ "$1 " == "text" ]];then case $2 in "1" ) echo '{"msgtype":"text","text":{"mentioned_list":["@all"],"content":"访问类型:' ${NETTYPE} '\n报警类型:' $3 '\n监控地址:' ${TARGETURL} '\n报警信息:' $4 '"}}' > text.json ;; "2" ) echo '{"msgtype":"text","text":{"mentioned_list":["@all"],"content":"访问类型:' ${NETTYPE} '\n报警类型:' $3 '\n监控地址:' ${TARGETURL} '\n报警信息:' $4 '异常标识校验值:' $5 '\n备注:网站预览图生成上传中."}}' > text.json ;; *) sleep 1 ;; esac sed -i 's#_#\\n#g' text.json curl ${WXMSGURL} -X POST -H "Content-Type:application/json" -d@text.json elif [[ "$1 " == "image" ]];then echo '{"msgtype":"image","image":{"base64":"' $2 '","md5":"' $3 '"}}' > data.json curl ${WXMSGURL} -X POST -H "Content-Type:application/json" -d@data.json elif [[ "$1 " == "markdown" ]];then if [[ "$2 " == "1" ]];then echo '{"msgtype":"markdown","markdown":{"content":"**' $3 '**\n> 访问类型:' ${NETTYPE} '访问\n> 应用状态信息:<font color=\"info\">\n' $4 '</font>"}}' > markdown.json sed -i 's#_#\\n#g' markdown.json fi curl ${WXMSGURL} -X POST -H "Content-Type:application/json" -d@markdown.json else sleep fi } function TargetMD5 (){ if [[ ! -d "${TARGETDIR} " ]];then echo "Create directory ${TARGETDIR} ....." ;mkdir -p $TARGETDIR ; fi if [[ ! -f "${TARGETFILE} " ]]; then curl -m 15 ${TARGETURL} -o ${TARGETFILE} ; fi export TARGETFILEMD5=$(md5sum ${TARGETFILE} | awk -F ' ' '{print $1}' ) } function Record (){ curl -m 20 ${TARGETURL} -o ${RECORDFILE} export RECORDFILEMD5="$(md5sum $_ | awk -F ' ' '{print $1}') " if [[ "${TARGETFILEMD5} MD5" != "${RECORDFILEMD5} MD5" ]]; then echo "${RECORDFILE} -${RECORDFILEMD5} " >> ${TARGETDIR} exception.log DIFFTEXT=$(diff --normal ${TARGETFILE} ${RECORDFILE} | egrep "^[0-9]" | tr '\n' '__' ) /usr/local /bin/phantomjs /usr/local /src/phantomjs/custom/screen-capture.js ${TARGETURL} ${RECORDFILE} .png IMGMD5="$(md5sum ${RECORDFILE}.png| awk -F ' ' '{print $1}') " IMGBASE64="$(base64 -w 0 < ${RECORDFILE}.png) " SendWXMsg "text" "2" "网站修改提醒" "被修改的行数:\n${DIFFTEXT} " "${RECORDFILEMD5} " sleep 1 SendWXMsg "image" "${IMGBASE64} " "${IMGMD5} " cp -f ${RECORDFILE} ${TARGETFILE} RCOUNT=RCOUNT${FLAG} let ${RCOUNT} +=1 export ${RCOUNT} =${!RCOUNT} if [[ ${!RCOUNT} -eq 1 ]];then cp -f ${RECORDFILE} ${TARGETFILE} export ${RCOUNT} =0 fi fi } function SiteMonitorCheck (){ STATUS=$(curl -I -m 10 -s -o /dev/null -w "%{http_code}" ${TARGETURL} ) if [[ $? -ne 0 ]];then STATUS="CLOSE" ;fi COUNT=COUNT${FLAG} let ${COUNT} +=1 if [[ "$STATUS " == "200" ]];then Record elif [[ "$STATUS " == "200" && ${!COUNT} -gt 2 ]];then export ${COUNT} =0 elif [[ "$STATUS " == "302" ]];then local LOCATION=$(curl -I -m 10 -s ${TARGETURL} | egrep "^Location" | tr -d '\r' | cut -d "/" -f 3) SendWXMsg "text" "1" "请求跳转异常地址" "_HTTP响应码:${STATUS} _跳转地址:${LOCATION} " elif [[ "$STATUS " == "CLOSE" && ${mcount} -le 2 ]];then SendWXMsg "text" "1" "访问异常" "外网无法访问该网站" export ${COUNT} =${!COUNT} continue else if [[ "${STATUS} " == "403" && "$(echo ${TARGETURL} | egrep -c "40081|30081") == '1'" ]];then Record continue else SendWXMsg "text" "1" "请求返回响应码异常" "HTTP响应码[${STATUS} ]" fi fi } function HealthCheck (){ if [[ "$NETTYPE " == "外网" ]];then local CHECK=$(curl -m 15 -o /dev/null -s -w "DNS解析耗时: " %{time_namelookup}"s_重定向耗时: " %{time_redirect}"s_TCP连接耗时: " %{time_connect}"s_请求准备耗时: " %{time_pretransfer}"s_应用连接耗时: " %{time_appconnect}"s_传输耗时: " %{time_starttransfer}"s_下载速度: " %{speed_download}"byte/s_整体请求响应耗时: " %{time_total}"s" "${TARGETURL} " ) if [[ $? -eq 0 ]];then SendWXMsg "markdown" "1" "${NETTYPE} -检查网站连接状态" "__> ${CHECK} " else SendWXMsg "markdown" "1" "${NETTYPE} -检查网站连接状态" "巡检地址: ${TARGETURL} __> 巡检信息: 访问异常" fi else local CHECK=$(curl -m 10 -o /dev/null -s -w "%{http_code}" "${TARGETURL} " ) if [[ $? -ne 0 || "$CHECK " != "200" ]];then export HCMSG="${HCMSG} __> 巡检地址: ${TARGETURL} _巡检信息: 异常" else echo . fi fi } function XKservice (){ if [[ "$NETTYPE " == "外网" ]];then local CHECK=$(echo $TARGETURL | egrep -c "xk" ) if [[ "$CHECK " == "1" ]];then curl -m 15 -s "${TARGETURL} /app/version" -o xk.json local STATUS=$(jq '"应用状态:"+(.code|tostring)+"_应用信息:"+(.msg|tostring)+"_当前应用版本:"+(.data.version)' xk.json | tr -d '"' ) SendWXMsg "markdown" "1" "应用埋点监控" "__> ${STATUS} " fi else local CHECK=$(echo "${TARGETURL} " | egrep -c "8010|9010" ) if [[ "$CHECK " == "1" ]];then curl -m 15 -s "${TARGETURL} /app/version" -o xk.json local STATUS=$(jq -M '"_Status:"+(.code|tostring)+"_Msg:"+(.msg|tostring)+"_Version:"+(.data.version)' xk.json | tr -d '"' ) export XKMSG="${XKMSG} __> 应用地址:${TARGETURL} _${STATUS} " fi fi } function InnerAppServices (){ if [[ "$(echo $TARGETURL| egrep -c '40081|30081') " == "1" ]];then curl -m 15 -s "${TARGETURL} /user/getVersion.htmls" -o innerApp.json STATUS=$(jq -M '"_Status:"+(.db|tostring)+"_Version:"+(.version|tostring)' innerApp.json | tr -d '"' ) export ZHCXMSG="${ZHCXMSG} __> 应用地址:${TARGETURL} _${STATUS} " fi } function main (){ DependencyCheck for i in $(cat ${MONITORSITE} );do CHECK=$(echo $i | egrep -c "^#" ) if [[ "$CHECK " == "1" ]];then continue ;fi export TARGETURL=$i export URL=$(echo $i |cut -f 3 -d '/' ) export TARGETDIR="/var/log/WebScreenCapture/${URL} /" export TARGETFILE="${TARGETDIR} index.html" export RECORDFILE="${TARGETDIR} $(date +%Y%m%d%H%M%S) -index.html" TargetMD5 if [[ "$1 " == "H" ]];then HealthCheck XKservice InnerAppServices else let FLAG+=1 export FLAG=${FLAG} SiteMonitorCheck fi done if [[ "$1 " == "H" && "$NETTYPE " == "内网" ]];then if [[ "${#HCMSG} " != "0" ]];then SendWXMsg "markdown" "1" "${NETTYPE} -业务应用运行情况巡查" "${HCMSG} _检测时间:$(date +%Y-%m-%d~%H:%M:%S) " else SendWXMsg "markdown" "1" "${NETTYPE} -业务应用运行情况巡查" "所有被监控业务正常_检测时间:$(date +%Y-%m-%d~%H:%M:%S) " fi ; if [[ "${#XKMSG} " != "0" ]];then SendWXMsg "markdown" "1" "${NETTYPE} -xk应用系统监控" "${XKMSG} _检测时间:$(date +%Y-%m-%d~%H:%M:%S) " fi if [[ "${#ZHCXMSG} " != "0" ]];then echo ${ZHCXKMSG} SendWXMsg "markdown" "1" "${NETTYPE} -zhcx应用系统监控" "${ZHCXMSG} _检测时间:$(date +%Y-%m-%d~%H:%M:%S) " fi fi } main $1 export FLAG=0
脚本执行及其结果:1 2 3 4 5 6 7 chmod +x /1.WebScreenCapture.sh ./1.WebScreenCapture.sh cd /var/log /WebScreenCapture/www.baidu.comls /var/log /WebScreenCapture/www.com 20230111172153-index.html 20230111172153-index.html.png 20230111173604-index.html data.json exception.log index.html text.json
weiyigeek.top-PhantomJS网站监控预警图
补充扩展 : 我们可以将该脚本加入到cron中定时每一分钟或3分钟执行一次监控。
1 2 3 $ crontab -e */1 * * * * bash -c /tmp/1.WebScreenCapture.sh
温馨提示: 当然你也可以将脚本进行修改,支持钉钉机器人以及自己编写的webhook实现QQ或者微信预警。
至此完毕,完毕更多运维奇技淫巧,请关注 【WeiyiGeek】哟。
0x0n 入坑出坑 问题1.在Ubuntu 22.04中安装phantomjs时报 libproviders.so: cannot open shared object file:
错误。
错误信息:1 2 3 4 5 6 $ phantomjs -v Auto configuration failed 139744413173696:error:25066067:DSO support routines:DLFCN_LOAD:could not load the shared library:dso_dlfcn.c:185:filename(libproviders.so): libproviders.so: cannot open shared object file: No such file or directory 139744413173696:error:25070067:DSO support routines:DSO_load:could not load the shared library:dso_lib.c:244: 139744413173696:error:0E07506E:configuration file routines:MODULE_LOAD_DSO:error loading dso:conf_mod.c:285:module=providers, path=providers 139744413173696:error:0E076071:configuration file routines:MODULE_RUN:unknown module name:conf_mod.c:222:module=providers
问题原因: 因为Ubuntu 22.04 使用新的 OpenSSL 版本 3.0.2 而不是旧的 OpenSSL 版本 1.1.1 ,这些 OpenSSL 版本不完全向后兼容,所以这就是为什么您在 PhantomJS 尝试自动配置 SSL/TLS 设置时看到此错误的原因。
1 2 openssl version OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
解决办法: export OPENSSL_CONF=/dev/null