#author("2021-06-23T17:24:32+09:00","","") [[SoftwareEngineering/ProgramLanguage/Java]] #author("2021-06-23T17:39:21+09:00","","") [[Java]] * Selenium [#u6e8d3b3] #contents * ページ全体のスクリーンショットを取得する [#dd142f35] ** ソースコード [#y165c340] *** Screenshot.java [#g008b76f] #highlightjs(java) package org.codereign.selenium.util; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import org.codereign.selenium.web.ScrollBar; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; public class Screenshot { private static final String CURRENT_DIRECTORY_PATH = System.getProperty("user.dir"); private Path defaultSaveDirectory; public Screenshot() { this(Paths.get(CURRENT_DIRECTORY_PATH, "画面のハードコピー")); } public Screenshot(final String defaultSaveDirectory) { this(Paths.get(defaultSaveDirectory)); } public Screenshot(Path defaultSaveDirectory) { this.defaultSaveDirectory = defaultSaveDirectory; } public File printScreen(WebDriver driver) throws IOException { final String fileName = "screenshot_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmmssSSS")) + ".png"; return printScreen(driver, fileName); } public File printScreen(WebDriver driver, final String fileName) throws IOException { // --------------------------------------------------------- // 事前準備 // --------------------------------------------------------- TakesScreenshot takesScreenshot = (TakesScreenshot) driver; ScrollBar scrollbar = new ScrollBar(driver); // --------------------------------------------------------- // スクロールバーのスクロールボックスの位置を初期化する // --------------------------------------------------------- scrollbar.resetPageOffset(); // --------------------------------------------------------- // 垂直方向にスクロールしながら画像を取得する // --------------------------------------------------------- List<BufferedImage> images = new ArrayList<BufferedImage>(); int diff = 0; BigDecimal previousScrollY = BigDecimal.valueOf(-1); BigDecimal currentScrollY = scrollbar.getPageYOffset(); while (previousScrollY.compareTo(currentScrollY) < 0) { File screenFile = takesScreenshot.getScreenshotAs(OutputType.FILE); BufferedImage scrapImage = ImageIO.read(screenFile); images.add(scrapImage); diff = currentScrollY.intValue() - previousScrollY.intValue(); previousScrollY = currentScrollY; currentScrollY = scrollbar.moveScrollY(scrapImage.getHeight()); } // --------------------------------------------------------- // 取得した画像をすべて結合する // ....結合後の画像の高さを算出する // --------------------------------------------------------- int totalHeight = 0; for (BufferedImage scrapImage : images) { totalHeight += scrapImage.getHeight(); } // --------------------------------------------------------- // ....複数枚の場合、最後の 2 枚を重ね合わせる必要があるため // ....その結合後の画像の高さを調整する // --------------------------------------------------------- if (1 < images.size()) { BufferedImage lastImage = images.get(images.size() - 1); totalHeight -= (lastImage.getHeight() - diff); } // --------------------------------------------------------- // ....複数の画像を 1 枚の画像に結合する // --------------------------------------------------------- BufferedImage entireImage = unionImage(images, totalHeight); // --------------------------------------------------------- // 画像を保存する // --------------------------------------------------------- return saveImage(entireImage, fileName); } /** * 複数の画像を 1 枚の画像に結合します。 * @param images * @param totalHeight * @return */ protected BufferedImage unionImage(List<BufferedImage> images, final int totalHeight) { // --------------------------------------------------------- // 最初の 1 枚を取得する // --------------------------------------------------------- BufferedImage firstImage = images.get(0); // --------------------------------------------------------- // 結合用画像の描画領域を取得する // --------------------------------------------------------- BufferedImage entireImage = new BufferedImage(firstImage.getWidth(), totalHeight, BufferedImage.TYPE_INT_ARGB); Graphics graphics = entireImage.getGraphics(); // --------------------------------------------------------- // 最初の 1 枚を結合用画像の描画領域に描画する // --------------------------------------------------------- graphics.drawImage(firstImage, 0, 0, null); int highWaterMark = firstImage.getHeight(); // --------------------------------------------------------- // 2 枚目以降(最後の 1 枚を除く)を結合用画像の描画領域に描画する // --------------------------------------------------------- for (int index = 1; index < images.size() - 1; index++) { BufferedImage scrapImage = images.get(index); graphics.drawImage(scrapImage, 0, highWaterMark, null); highWaterMark += scrapImage.getHeight(); } // --------------------------------------------------------- // 最後の 1 枚を結合用画像の描画領域に描画する // ....最後の 1 枚は今まで描画した領域と重なる箇所があることに注意 // --------------------------------------------------------- if (1 < images.size()) { BufferedImage lastImage = images.get(images.size() - 1); graphics.drawImage(lastImage, 0, totalHeight - lastImage.getHeight(), null); } // --------------------------------------------------------- // 結合用画像を返却する // --------------------------------------------------------- return entireImage; } /** * 画像を保存します。 * @param bufferedImage * @param fileName * @return * @throws IOException */ protected File saveImage(BufferedImage bufferedImage, final String fileName) throws IOException { if (Files.notExists(defaultSaveDirectory)) { Files.createDirectories(defaultSaveDirectory); } File file = new File(defaultSaveDirectory.toString(), fileName); ImageIO.write(bufferedImage, "png", file); System.out.println("スクリーンショットを保存しました。[" + file.getAbsolutePath() + "]"); return file; } } *** ScrollBar.java [#k8522f2b] #highlightjs(java) package org.codereign.selenium.web; import java.math.BigDecimal; import java.text.MessageFormat; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; public class ScrollBar { private final JavascriptExecutor executor; private final MessageFormat scrollTo = new MessageFormat("scrollTo({0}, {1});"); private final MessageFormat scrollBy = new MessageFormat("scrollBy({0}, {1});"); private final MessageFormat pageYOffset = new MessageFormat("return String(window.pageYOffset);"); /** * 指定された {@code JavascriptExecutor} を持つ、新しい {@code ScrollBar} オブジェクトを構築します。 * @param driver selenium web driver */ public ScrollBar(WebDriver driver) { this.executor = (JavascriptExecutor) driver; } /** * 指定された {@code JavascriptExecutor} を持つ、新しい {@code ScrollBar} オブジェクトを構築します。 * @param driver selenium javascript executor */ public ScrollBar(JavascriptExecutor executor) { this.executor = executor; } /** * スクロールバーのスクロールボックスの位置を初期化します。<br> * 垂直スクロールバーのスクロールボックスは一番上<br> * 水平スクロールバーのスクロールボックスは左端<br> */ public void resetPageOffset() { final Object arguments = new Object[] { 0, 0 }; executor.executeScript(scrollTo.format(arguments)); } /** * 垂直スクロールバーのスクロールボックスの位置を初期化します。 * @return 垂直スクロールバーのスクロールボックスの位置 */ public BigDecimal getPageYOffset() { final Object arguments = new Object[] {}; final String statement = pageYOffset.format(arguments); String pageYOffset = (String) executor.executeScript(statement); return stringToBigDecimal(pageYOffset); } /** * 垂直スクロールバーのスクロールボックスの位置を移動します。 * @param y 現在の垂直スクロールバーのスクロールボックスの位置からの移動量 * @return 移動後の垂直スクロールバーのスクロールボックスの位置 */ public BigDecimal moveScrollY(int y) { final Object arguments = new Object[] { 0, y }; executor.executeScript(scrollBy.format(arguments)); return getPageYOffset(); } /** * 文字列を {@code BigDecimal} 型に変換して返却します。 * 変換できない場合は、[@code null} を返却します。 * @param value 変換する文字列 * @return [{@code BigDecimal} 型の値 */ private BigDecimal stringToBigDecimal(final String value) { try { return new BigDecimal(value); } catch (NumberFormatException e) { return null; } } }