java использует phantomjs для скриншотов

Java задняя часть JavaScript PhantomJS

Я периодически искал информацию и проверял различные методы реализации. Наконец, я в основном получил скриншот страницы. Поскольку промежуточный процесс занимает много времени, я поделюсь им, чтобы помочь вам быстро реализовать скриншот.

Почему выбирают фантомы для скриншотов

Есть много способов сделать снимок экрана, например:

  • selenium
  • HtmlUnit
  • Html2Изображение , и так далее Но скриншоты этих реализаций не годятся. Selenium может добиться только скриншотов, а не всей страницы, а HtmlUnit, Html2Image поддерживает js не очень хорошо, и на скриншотах будет много пробелов. phantomjs — это эфирное масло, он может перехватить всю страницу и хорошо влияет на поддержку js.

Предварительная подготовка

Установите фантомы. мак ос

brew install phantomjs

Скриншот командной строки

Мы можем взять небольшой резак после установки.

  • Откройте терминал и введите следующую команду:
/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs
/Users/hetiantian/SoftWares/phantomjs/examples/rasterize.js
https://juejin.cn/post/6844903686821396487
/Users/hetiantian/Desktop/juejin-command.png
  • Посмотреть эффект
    juejin-command.png
    Обнаружил, что картинка не загружается должным образом
    难受

Взгляните на командную строку только что:/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs: адрес сохранения исполняемого файла phantomjs/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 };Вопрос 🤔️ Увеличьте высоту в десять раз и обнаружите, что это в основном идеальный снимок экрана, но если страница очень короткая, вы найдете недостатки, оставив внизу большое пустое место. причина:page.viewportSize = { width: 600, height: 600 };Размер начального открытого браузера установлен, и js можно загрузить, увеличив это значение. Если бы мы могли получить фактический размер страницы при установке размера высоты, но нет, я не могу.

难受.jpeg

И недопустимо заранее ставить большое значение высоты, например 30000, т.к. недопустим эффект пустого пространства внизу Попытка вторая: Добавьте следующий код перед методом window.setTimeout

 page.evaluate(function(){
     scrollBy(0, 18000); 
});

К сожалению, цикл for не может использоваться в оценке, шлак фронтенда действительно не знает, как его изменить, поэтому я сдался.

Делаем скриншоты в 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>

  • Код
public class PhantomjsTest2 {
    public static void main(String[] args) throws InterruptedException, IOException {
        //设置必要参数
        DesiredCapabilities dcaps = new DesiredCapabilities();
        //ssl证书支持
        dcaps.setCapability("acceptSslCerts", true);
        //截屏支持
        dcaps.setCapability("takesScreenshot", true);
        //css搜索支持
        dcaps.setCapability("cssSelectorsEnabled", true);
        //js支持
        dcaps.setJavascriptEnabled(true);
        //驱动支持(第二参数表明的是你的phantomjs引擎所在的路径)
        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
                "/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs");
        //创建无界面浏览器对象
        PhantomJSDriver driver = new PhantomJSDriver(dcaps);

        //设置隐性等待(作用于全局)
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
        long start = System.currentTimeMillis();
        //打开页面
        driver.get("https://juejin.cn/post/6844903686821396487");
        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做为参数传递给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) + " 毫秒");
    }
}

Комментарии уже достаточно подробные. Единственное, что нужно сказать: скольжение страниц достигается за счет выполнения js-кода, и каждое скольжение будет гарантировать, что есть время для загрузки js через сон. Он вызовет 33 раза скольжения, потому что максимальная высота перехвата phantomjs составляет 32767 пикселей, поэтому 33-кратное скольжение может гарантировать, что большая часть страницы, которая может быть перехвачена, уже загружена. Вложение: Разница между window.scrollBy(0,1000) и window.scrollTo(0,1000)

window.scrollBy(0,1000)
window.scrollBy(0,1000)
执行到这里页面滑动1000+1000px
window.scrollTo(0,1000)
window.scrollTo(0,1000)
执行到这里页面滑动到1000px处

window.scrollTo(0, document.body.scrollHeightМожно провести пальцем вниз по странице, не выбранной по двум причинам: 1) Сразу смахните вниз и js не загрузится вовремя 2) Некоторые страницы не имеют нижней части и их можно постоянно пролистывать для загрузки. Примечание. Упомянутый здесь js слишком поздно для загрузки относится к: js, который хочет перехватить страницу, слишком поздно для загрузки Недостаток этого метода: он занимает много времени. Конечно же, у медведя и у рыбы не может быть и того, и другого: по статистике, чтобы сделать снимок, требуется около четырех минут.

====================Обновлено 2018.10.15======================

Недостатки фантомов

  • Максимальная длина скриншота 32767 пикселей.
  • Могут возникнуть проблемы с пересечением
  • Требуется драйвер браузера

Вы можете использовать Google Puppeteer для завершения функции скриншота: С простой демонстрацией:

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);
        })
    });
}