Goalist Developers Blog

Selenium WebDriverでスクリーンショットを撮る

こんにちは ゴーリスト開発のモリツグです

Selenium WebDriverで画像キャプチャが必要になった時、Firefoxだとスクロールで見えない部分まで撮影できるのに、Chromeだと見えている部分しか撮影してくれません。
この問題を解決してくれる便利なライブラリaShotのご紹介です。
言語はJavaです。

aShotの使い方

まずは以下のReadMeを参考にpom.xmlをかいたり、直接jarを落としてきたり好きな方法でaShotを使えるようにします。

github.com

Mavenリポジトリのリンクはこちらです

以下のような感じで撮影してファイルに書き出せばよいと思います。

private void captureImage(WebDriver driver, String filePath, float quality) throws IOException {
    JPEGImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault());
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(quality);
        
    // 150はミリ秒でスクロールを待つ時間、短すぎるとダメな模様
    ShootingStrategy ss = ShootingStrategies.viewportPasting(150); 
    // スクロールせずにそのまま撮影したい場合は以下
    // ShootingStrategy ss = ShootingStrategies.simple();

    Screenshot screenshot = new AShot().shootingStrategy(ss).takeScreenshot(driver);

    ImageWriter writer = null;
    try {
        writer = ImageIO.getImageWritersByFormatName("jpg").next();
        writer.setOutput(ImageIO.createImageOutputStream(new File(filePath)));
        writer.write(null, new IIOImage(screenshot.getImage(), null, null), param);
    } finally {
        if (writer != null) {
            writer.dispose();
        }
    }
}

複数の画像を連結したい!

aShotとは直接関係はありませんが、複数の画像を1枚につなげて保存したいという要望があると思います。
画像サイズは同じであるとして、以下のような感じでできます。(厳密には横のサイズが1枚目と同じ前提)

private void createAllImage(WebDriver driver, String filePath) throws IOException {
    ShootingStrategy ss = ShootingStrategies.viewportPasting(150); 
    
    driver.navigate().to("https://www.google.co.jp/");
    BufferedImage image1 = new AShot().shootingStrategy(ss).takeScreenshot(driver).getImage();
    
    driver.navigate().to("https://goalist.co.jp/");
    BufferedImage image2 = new AShot().shootingStrategy(ss).takeScreenshot(driver).getImage();
    
    JPEGImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault());
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(1.0f);
    
    List<BufferedImage> captureList = new ArrayList<BufferedImage>();
    captureList.add(image1);
    captureList.add(image2);

    int entireHeight = 0;
    for (BufferedImage imageBuf : captureList) {
        entireHeight += imageBuf.getHeight();
    }
    BufferedImage entireImage
        = new BufferedImage(captureList.get(0).getWidth(), entireHeight, captureList.get(0).getType());

    ImageWriter writer = null;
    Graphics entireGraphic = null;
    try {
        entireGraphic = entireImage.getGraphics();
        int tempHeight = 0;
        for (BufferedImage imageBuf : captureList) {
            entireGraphic.drawImage(imageBuf, 0, tempHeight, null);
            tempHeight += imageBuf.getHeight();
        }
        writer = ImageIO.getImageWritersByFormatName("jpg").next();
        writer.setOutput(ImageIO.createImageOutputStream(new File(filePath)));
        writer.write(null, new IIOImage(entireImage, null, null), param);
    } finally {
        if (writer != null) {
            writer.dispose();
        }
        if (entireGraphic != null) {
            entireGraphic.dispose();
        }
    }
}

まとめ

ブラウザに関係なくお手軽にスクリーンショットが取れるaShotが本当に便利でした。
スクロールバーは出ているけれど、このパターンの時はスクロールしなくていいなぁみたいな時にShootingStrategyでスクロールさせない方法が指定できるのも使いやすかったです。