斷斷續(xù)續(xù)查找資料躬存、驗證不同的實現(xiàn)方法終于算基本搞定了頁面截圖端壳,因為中間過程曲折花費較多時間,分享出來幫助大家快速實現(xiàn)截圖
為什么選用phantomjs進行截圖
截圖可以實現(xiàn)的方式有很多,比如:
- selenium
- HtmlUnit
- Html2Image
稠曼、碱屁、磷脯、and so on
但是這些實現(xiàn)的截圖效果都不好。selenium只能實現(xiàn)截屏娩脾,不能截取整個頁面赵誓,而HtmlUnit、Html2Image對js的支持效果并不好,截下來的圖會有很多空白俩功。phantomjs就是萬精油了幻枉,既能截取整個頁面,對js支持的效果又好
前期準備
安裝phantomjs诡蜓。mac os
brew install phantomjs
命令行的方式進行截圖
安裝以后我們就可以小試牛刀了
- 打開終端熬甫,輸入以下命令:
/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs
/Users/hetiantian/SoftWares/phantomjs/examples/rasterize.js
https://juejin.im/post/5bb24bafe51d450e4437fd96
/Users/hetiantian/Desktop/juejin-command.png
-
查看效果
juejin-command.png
發(fā)現(xiàn)圖片沒有加載好
難受
來看以下剛剛的命令行:
/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs
:phantomjs可執(zhí)行文件保存地址
/Users/hetiantian/SoftWares/phantomjs/examples/rasterize.js
:rasterize.js文件地址
這段命令可以理解為用phantomjs去運行rasterize.js文件,所以要想解決圖片空白的問題我們需要去看一下rasterize.js文件蔓罚。
"use strict";
var page = require('webpage').create(),
system = require('system'),
address, output, size, pageWidth, pageHeight;
if (system.args.length < 3 || system.args.length > 5) {
console.log('Usage: rasterize.js URL filename [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(1);
} else {
address = system.args[1];
output = system.args[2];
page.viewportSize = { width: 600, height: 600 };
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
size = system.args[3].split('*');
page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
: { format: system.args[3], 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, height: pageHeight };
} else {
console.log("size:", system.args[3]);
pageWidth = parseInt(system.args[3], 10);
pageHeight = parseInt(pageWidth * 3/4, 10); // it's as good an assumption as any
console.log ("pageHeight:",pageHeight);
page.viewportSize = { width: pageWidth, height: pageHeight };
}
}
if (system.args.length > 4) {
page.zoomFactor = system.args[4];
}
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit(1);
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});
}
嘗試一:
對page.viewportSize = { width: 600, height: 600 };
產生了疑問???
把height調大十倍椿肩,發(fā)現(xiàn)基本是完美截圖了,但是如果頁面的篇幅特別短豺谈,會發(fā)現(xiàn)有瑕疵郑象,下面留有一大片空白。原因:page.viewportSize = { width: 600, height: 600 };
設置的是初始打開瀏覽器的大小茬末,通過增大這個值可以加載js厂榛。如果我們能拿到實際頁面的大小在設置height大小,但是不丽惭,我不能击奶。
并且不能接受預先設定一個很大的height值,比如30000吐根,因為不能接受底下留白的效果
嘗試二:
在window.setTimeout方法之前加入以下代碼
page.evaluate(function(){
scrollBy(0, 18000);
});
無奈evaluate里不能在用for循環(huán)了正歼,前端渣渣真的不知道如何改,遂放棄
java代碼方式進行截圖
- 需要的依賴
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.45.0</version>
</dependency>
<dependency>
<groupId>com.codeborne</groupId>
<artifactId>phantomjsdriver</artifactId>
<version>1.2.1</version>
<!-- this will _always_ be behind -->
<exclusions>
<exclusion>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</exclusion>
<exclusion>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
- 代碼實現(xiàn)
public class PhantomjsTest2 {
public static void main(String[] args) throws InterruptedException, IOException {
//設置必要參數(shù)
DesiredCapabilities dcaps = new DesiredCapabilities();
//ssl證書支持
dcaps.setCapability("acceptSslCerts", true);
//截屏支持
dcaps.setCapability("takesScreenshot", true);
//css搜索支持
dcaps.setCapability("cssSelectorsEnabled", true);
//js支持
dcaps.setJavascriptEnabled(true);
//驅動支持(第二參數(shù)表明的是你的phantomjs引擎所在的路徑)
dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
"/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs");
//創(chuàng)建無界面瀏覽器對象
PhantomJSDriver driver = new PhantomJSDriver(dcaps);
//設置隱性等待(作用于全局)
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
long start = System.currentTimeMillis();
//打開頁面
driver.get("https://juejin.im/post/5bb24bafe51d450e4437fd96");
Thread.sleep(30 * 1000);
JavascriptExecutor js = driver;
for (int i = 0; i < 33; i++) {
js.executeScript("window.scrollBy(0,1000)");
//睡眠10s等js加載完成
Thread.sleep(5 * 1000);
}
//指定了OutputType.FILE做為參數(shù)傳遞給getScreenshotAs()方法拷橘,其含義是將截取的屏幕以文件形式返回局义。
File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
Thread.sleep(3000);
//利用FileUtils工具類的copyFile()方法保存getScreenshotAs()返回的文件對象
FileUtils.copyFile(srcFile, new File("/Users/hetiantian/Desktop/juejin-01.png"));
System.out.println("耗時:" + (System.currentTimeMillis() - start) + " 毫秒");
}
}
注釋已經夠詳細了不多說了。唯一說一點:通過去執(zhí)行js代碼實現(xiàn)頁面滑動冗疮,并且每次滑動都會通過睡眠保證有時間可以將 js加載進來萄唇。會調用33次滑動,因為phantomjs截取最大的高度為32767px(int 32位的最大整數(shù))术幔,所以滑動33次可以保證能夠截取到的最大頁面部分其js已經是加載完成了的
附:window.scrollBy(0,1000)另萤、window.scrollTo(0,1000)的區(qū)別
window.scrollBy(0,1000)
window.scrollBy(0,1000)
執(zhí)行到這里頁面滑動1000+1000px
window.scrollTo(0,1000)
window.scrollTo(0,1000)
執(zhí)行到這里頁面滑動到1000px處
window.scrollTo(0, document.body.scrollHeight
可以滑動到頁面底部,不選擇有兩個原因:
1)一下子滑動到底部js會來不及被加載
2)有些頁面沒有底部诅挑,可以一直滑動加載
注:這里所說的js來不及加載指的是:想要截取頁面的js來不及加載
該方式的缺點:比較費時間四敞。果然熊和魚掌不可兼得也,統(tǒng)計了一下截取一張圖片大概需要四分多鐘
===================更新于2018.10.15====================
phantomjs的缺點
- 有最大截圖長度32767px
- 可能會出現(xiàn)跨越的問題
- 需要裝瀏覽器驅動
可以使用google的puppeteer完成截圖功能:
附簡單demo:
const puppeteer = require('/usr/local/lib/node_modules/puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
// await page.goto('https://www.zhihu.com/question/22263777');
await page.goto('http://www.iqiyi.com');
await page.setViewport({
width: 1200,
height: 800
});
await autoScroll(page);
await page.screenshot({
path: 'jd.png',
fullPage: true
});
await browser.close();
})();
function autoScroll(page) {
return page.evaluate(() => {
return new Promise((resolve, reject) => {
var totalHeight = 0;
var distance = 100;
var timer = setInterval(() => {
var scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= scrollHeight) {
clearInterval(timer);
resolve();
}
}, 100);
})
});
}