PhantomJS to screenshot website div for Spring MVC

2019-04-10 00:03发布

问题:

I've been introduced to the power of PhantomJS and CasperJS to take website screenshots. The article "Responsive Screenshots With Casper" was really helpful to learn about the basics of the two technologies plus the screenshot feature.

Next is to how can this technology be used for integration with a Spring MVC application.

So far I have tried "Screen Capture using PhantomJS, GhostDriver, Selenium Hub"'s code and combined it with the answer provided in "Remote PhantomJS driver in Junit".

What happened is that every time I run the program, I keeps having a ClassNotFoundError and I kept providing the missing JAR files. I ended up downloading and providing nine new JAR files in my Spring application:

  • phantomjsdriver-1.0.4.jar
  • selenium-java-2.39.0.jar and its source file
  • asm-all-3.3.1.jar
  • cglib-3.1.jar
  • commons-exec-1.2.jar
  • guava-16.0.1.jar
  • httpclient-4.3.2.jar
  • httpcore-4.3.2.jar
  • json-20140107.jar

until the error below showed

SEVERE: Servlet.service() for servlet [spring] in context with path [/my_spring_app] threw exception [Handler processing failed; nested exception is java.lang.IncompatibleClassChangeError: class net.sf.cglib.core.DebuggingClassWriter has interface org.objectweb.asm.ClassVisitor as super class] with root cause
java.lang.IncompatibleClassChangeError: class net.sf.cglib.core.DebuggingClassWriter has interface org.objectweb.asm.ClassVisitor as super class

Should I keep trying this path just to get a screenshot of my website? I feel like it's a losing battle based on what's going on. I'm not familiar with PhantomJS and can't find any legitimate tutorial or guide online for this setup. I would go back to iText and JFreeCharts if I can't make this work.

Below is the code I use within my Spring application:

package my_spring_app.controller;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MyController {

    protected final static Log logger = LogFactory
            .getLog(MyController.class);

    @RequestMapping(value = "/screenshot", method = RequestMethod.GET)
    public String showSupplementsPage(ModelMap model,
            HttpServletRequest request, HttpServletResponse response) {

        tryPhantom();

        return "screenshot";
    }

    private URI uri;
    private static PhantomJSDriverService service;
    private WebDriver webDriver;
    protected static DesiredCapabilities dCaps;

    public void tryPhantom() {

        service = new PhantomJSDriverService.Builder()
                .usingPhantomJSExecutable(new File("classpath:phantomjs.exe"))
                .usingAnyFreePort().build();
        try {
            service.start();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            uri = new URI("http://localhost:8080/");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        dCaps = new DesiredCapabilities();
        dCaps.setJavascriptEnabled(true);
        dCaps.setCapability("takesScreenshot", true);

        webDriver = new RemoteWebDriver(service.getUrl(), dCaps);
        webDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

        long iStart = System.currentTimeMillis();
        webDriver.get("http://localhost:8080/");

        webDriver = new Augmenter().augment(webDriver);
        File srcFile = ((TakesScreenshot) webDriver)
                .getScreenshotAs(OutputType.FILE);
        System.out.println("File:" + srcFile);
        try {
            FileUtils.copyFile(srcFile, new File("classpath:screenshots/pic.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Single Page Time:"
                + (System.currentTimeMillis() - iStart));

        webDriver.quit();
        service.stop();
    }

}

Here is a diagram of what I have in mind:

回答1:

Should I keep trying this path just to get a screenshot of my website? I feel like it's a losing battle based on what's going on.

You are right, I think the approach of managing dependencies needs to be reconsidered, otherwise it will not be possible to make the site work. Donwloading jars manually one by one is unlikely to give good results, as there is no guarantee that the library versions are compatible with each other.

In fact the IncompatibleClassChangeErroris a sign that the jars downloaded are not compatible.

Your best bet is to use Maven to donwload most of the jars automatically. By declaring only the top level jars, Maven will then download the needed dependent jars (transitive dependencies) automatically, helping reduce and in many cases eliminate library incompatibility problems.

Once you have the site up and working, it's a matter of writing some scripts in Casper.js to access a page in your local server, click some buttons to generated PDFs etc. and then take some snapshots.

There is no special integration needed with Spring MVC, for the Spring MVC application it's transparent if the site is accessed by PhantomJS or a normal browser.