5

Ich möchte einige Screenshots meiner Hybrid-App für iTunes automatisch verbinden. Ich benutze Ubuntu 14.04. chromedriver 2.15.322448Wie man Retina Screenshots mit Xvfb und Selenium macht

Mit Selenium und Xvfb lassen sich Screenshots automatisch erstellen. Aber es ist nicht einfach, Retina-Screenshots zu bekommen.

begann ich meine Xvfb mit einem höheren dpi:

/usr/bin/Xvfb :99 -screen 0 2000x2000x24 -dpi 200 

Wenn ich die Anzeigeinformationen überprüfen alles scheint richtig zu sein:

xdpyinfo -display :99 

... 
screen #0: 
    dimensions: 2000x2000 pixels (254x254 millimeters) 
    resolution: 200x200 dots per inch 
    depths (6): 24, 1, 4, 8, 16, 32 
... 

ich meine chromedriver Dann starten wie folgt

private WebDriver getChromeDriver (Phone phone) 
{ 
    Map<String, Object> deviceMetrics = new HashMap<String, Object>(); 
    deviceMetrics.put("width", 320); 
    deviceMetrics.put("height", 460); 
    deviceMetrics.put("pixelRatio", 2); 
    Map<String, Object> mobileEmulation = new HashMap<String, Object>(); 
    mobileEmulation.put("deviceMetrics", deviceMetrics); 
    mobileEmulation.put("userAgent", "iphone4"); 

    ChromeDriverService cds = new ChromeDriverService.Builder().withEnvironment(ImmutableMap.of("DISPLAY", ":99")).build(); 

    Map<String, Object> chromeOptions = new HashMap<String, Object>(); 
    chromeOptions.put("mobileEmulation", mobileEmulation); 
    DesiredCapabilities capabilities = DesiredCapabilities.chrome(); 
    capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); 
    WebDriver driver = new ChromeDriver(cds, capabilities); 
    return driver; 
} 

und nach einigen anderen langweiligen Code, nehme ich den Screenshot:

File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); 

Dies funktioniert nicht. Der Screenshot ist in normalen dpi. Das Bild der Website ist also nur 320x460 und nicht 640x960, wie es sein sollte.

stelle ich einen Haltepunkt kurz vor dem Screenshot gemacht und schüttete den Framebuffer wie folgt aus:

export DISPLAY=:99 
xwd -root -silent | xwdtopnm |pnmtojpeg > screen.jpg 

Result of xwd dumping the content of the virtual framebuffer

Wie Sie in der Titelleiste sehen in Bezug auf höhere dpi, aber der Rest gemacht wird des Browserfensters nicht.

Also wie kann ich einen Chrom-Treiber mit mehr dpi laufen lassen, um Retina-Screenshots zu machen? Ist es möglich?

+0

Haben Sie eine Lösung gefunden?Ich bin bei genau dem gleichen Problem stecken, würde es begrüßen, wenn Sie Ihre Erfahrung mit xvfb und Retina Auflösung Screenshots – Alex

+1

Ich habe eine Antwort unten gepostet. Ich hoffe, es hilft. – Janning

Antwort

1

Wenn Sie nur ein paar screenhosts nehmen möchten, können Sie google chrome ohne Kopf-Tool verwenden. Zum Beispiel bekommen Sie einen Retina-Screenshot so einfach wie

$ google-chrome --headless --hide-scrollbars --disable-gpu \ 
       --screenshot --force-device-scale-factor=2 \ 
       --window-size=750,1334 https://www.kicktipp.de/ 
1

Ich bin mit dem gleichen Problem konfrontiert und immer noch fest, aber das Folgende kann nützlich sein. Es erlaubte mir, entweder xvfb oder chrome auszuschließen, indem ich eine VNC Verbindung an den xvfb Framebuffer anschloss.

#!/bin/bash 
export GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH" 

function shutdown { 
    kill -s SIGTERM $NODE_PID 
    wait $NODE_PID 
} 

sudo -E -i -u seluser \ 
    DISPLAY=$DISPLAY \ 
    xvfb-run --server-args="$DISPLAY -screen 0 $GEOMETRY -dpi 300 -ac +extension RANDR" \ 
    java -jar /opt/selenium/selenium-server-standalone.jar & 
NODE_PID=$! 

trap shutdown SIGTERM SIGINT 
for i in $(seq 1 10) 
do 
    xdpyinfo -display $DISPLAY >/dev/null 2>&1 
    if [ $? -eq 0 ]; then 
    break 
    fi 
    echo Waiting xvfb... 
    sleep 0.5 
done 

fluxbox -display $DISPLAY & 

x11vnc -forever -usepw -shared -rfbport 5900 -display $DISPLAY & 

wait $NODE_PID 

Nach VNC'ing in kann google-chrome GUI vom Endgerät geladen werden. Die Navigation zu Webseiten bestätigt, dass Chrome die Seiten mit der korrekten DPI rendert. Screenshot http://i.stack.imgur.com/iEjo0.jpg

Ich würde wirklich gerne das funktioniert auch so bitte kontaktieren Sie, wenn Sie neue Entwicklungen haben. Ich benutzte https://registry.hub.docker.com/u/selenium/standalone-chrome-debug/ BTW.

0

Ich wechselte zu Firefox und es funktionierte für mich mit dem folgenden Code. Aber im Moment funktioniert es nicht so gut wie Selen mit meiner Firefox Version 47, siehe https://github.com/SeleniumHQ/selenium/issues/2257. Also kann ich diesen Code jetzt nicht testen, aber beim letzten Mal konnte ich damit Retina Screenshots bekommen:

package de.kicktipp.screenshots.stackoverflow; 

import java.awt.Image; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 

import javax.imageio.ImageIO; 

import org.openqa.selenium.Dimension; 
import org.openqa.selenium.JavascriptExecutor; 
import org.openqa.selenium.WebDriver; 
import org.openqa.selenium.firefox.FirefoxBinary; 
import org.openqa.selenium.firefox.FirefoxDriver; 
import org.openqa.selenium.firefox.FirefoxProfile; 

public class ScreenshotMaker 
{ 
    private PhoneList   phoneList = new PhoneList(); 

    private static final String HOST  = "https://m.kicktipp.de"; 
    private static final String PATH  = "/"; 

    private File    resultDirectory; 
    private int     filenumber = 0; 

    public static void main (String[] args) throws Exception 
    { 
     ScreenshotMaker screenshotMaker = new ScreenshotMaker(); 
     screenshotMaker.run(); 
    } 

    public WebDriver getDriver (Phone phone, Display display) 
    { 
     FirefoxProfile profile = new FirefoxProfile(); 
     // profile.setPreference("layout.css.devPixelsPerPx", "2.0"); 
     // Ansonsten erscheint ein hässliches Popup welches Reader Funktion 
     // anbietet 
     profile.setPreference("reader.parse-on-load.enabled", false); 
     profile.setPreference("xpinstall.signatures.required", false); 
     FirefoxBinary firefoxBinary = new FirefoxBinary(); 
     firefoxBinary.setEnvironmentProperty("DISPLAY", display.getDisplayNumberString()); 
     FirefoxDriver firefoxDriver = new FirefoxDriver(firefoxBinary, profile); 
     firefoxDriver.manage().window().setSize(new Dimension(phone.getWidth(), display.getHeight())); 
     return firefoxDriver; 
    } 

    private void run () throws Exception 
    { 
     mkdir(); 
     for (Phone phone : phoneList) 
     { 
      WebDriver driver = null; 
      Display display = null; 
      try 
      { 
       display = new Display(phone.getDpiFaktor()); 
       driver = getDriver(phone, display); 
       System.out.println(phone.getName()); 
       filenumber = 0; 
       System.out.println(""); 
       System.out.println("Generating Screenshots for " + phone.getName()); 
       System.out.println("-----------------------------------------------------------------------------"); 
       driver.get(HOST + "/"); 
       shot(display, driver, PATH, phone); 
      } 
      finally 
      { 
       if (driver != null) 
       { 
        driver.quit(); 
       } 
       if (display != null) 
       { 
        display.shutdown(); 
       } 
      } 
     } 
     System.out.println(""); 
     System.out.println("-----------------------------------------------------------------------------"); 
     System.out.println("Finished."); 

    } 

    private void mkdir () throws IOException 
    { 
     File targetDir = targetDir(); 
     resultDirectory = new File(targetDir, "results"); 
     resultDirectory.mkdir(); 
     System.out.println("Writing screenshots to " + resultDirectory.getCanonicalPath()); 
    } 

    public File targetDir () 
    { 
     String relPath = getClass().getProtectionDomain().getCodeSource().getLocation().getFile(); 
     File targetDir = new File(relPath + "../.."); 
     if (!targetDir.exists()) 
     { 
      targetDir.mkdir(); 
     } 
     return targetDir; 
    } 

    private void shot (Display display, WebDriver driver, String path, Phone phoneSpec) throws Exception 
    { 
     String url = getUrl(path); 
     driver.get(url); 
     scrollToRemoveScrollbars(driver); 
     // Selenium screenshot doesn't work, we are dumping the framebuffer 
     // directly 
     File srcFile = display.captureScreenshot(); 
     moveFile(srcFile, phoneSpec); 
    } 

    private void scrollToRemoveScrollbars (WebDriver driver) throws Exception 
    { 
     JavascriptExecutor js = (JavascriptExecutor) driver; 
     js.executeScript("window.scrollTo(0,20);"); 
     js.executeScript("window.scrollTo(0,0);"); 
     Thread.sleep(800); 
    } 

    private String getUrl (String path) 
    { 
     StringBuffer url = new StringBuffer(HOST); 
     url.append(path); 
     return url.toString(); 
    } 

    private void moveFile (File srcFile, Phone phone) throws Exception 
    { 
     String filename = phone.getFilename(filenumber); 
     File file = new File(resultDirectory, filename); 
     if (file.exists()) 
     { 
      file.delete(); 
     } 
     crop(srcFile, file, phone); 
     System.out.println(filename); 
    } 

    private void crop (File srcFile, File targetFile, Phone phone) throws Exception 
    { 
     int width = phone.getPixelWidth(); 
     int height = phone.getPixelHeight(); 
     int yStart = 71 * phone.getDpiFaktor(); 
     Image orig = ImageIO.read(srcFile); 
     BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 
     bi.getGraphics().drawImage(orig, 0, 0, width, height, 0, yStart, width, height + yStart, null); 
     ImageIO.write(bi, "png", targetFile); 
    } 
} 

class PhoneList extends ArrayList<Phone> 
{ 
    private static final Phone IPHONE_4  = new Phone(320, 460, "iphone4", 2); 
    private static final Phone IPHONE_5  = new Phone(320, 548, "iphone5", 2); 
    private static final Phone IPHONE_6  = new Phone(375, 667, "iphone6", 2); 
    private static final Phone IPAD   = new Phone(1024, 748, "ipad", 2); 
    private static final Phone IPHONE_6_PLUS = new Phone(414, 736, "iphone6plus", 3); 
    private static final Phone AMAZON   = new Phone(480, 800, "amazon", 1); 

    public PhoneList() 
    { 
     add(AMAZON); 
     add(IPHONE_4); 
     add(IPHONE_5); 
     add(IPHONE_6); 
     add(IPAD); 
     add(IPHONE_6_PLUS); 
    } 
} 

class Phone 
{ 
    private int  width  = 0; 
    private int  height  = 0; 
    private String name  = ""; 
    private int  dpiFaktor = 2; 

    public Phone (int width, int height, String name, int dpiFaktor) 
    { 
     this.width = width; 
     this.height = height; 
     this.name = name; 
     this.dpiFaktor = dpiFaktor; 
    } 

    public int getWidth () 
    { 
     return width; 
    } 

    public int getHeight () 
    { 
     return height; 
    } 

    public int getPixelWidth () 
    { 
     return width * dpiFaktor; 
    } 

    public int getPixelHeight () 
    { 
     return height * dpiFaktor; 
    } 

    public int getDpiFaktor () 
    { 
     return dpiFaktor; 
    } 

    public String getName () 
    { 
     return name; 
    } 

    public Dimension getDimension () 
    { 
     return new Dimension(width, height); 
    } 

    public String getFilename (int number) 
    { 
     String dimension = getPixelWidth() + "x" + getPixelHeight(); 
     return name + "-" + dimension + "-" + number + ".png"; 
    } 
} 

class Display 
{ 
    private static final int HEIGHT     = 5000; 
    private static final int WIDTH     = 5000; 
    private static String  XVFB     = "/usr/bin/Xvfb"; 
    private static String  DISPLAY_NUMBER_STRING = ":99"; 
    private static String  SCREEN_SIZE    = " -screen 0 " + WIDTH + "x" + HEIGHT + "x24"; 
    private static String  XVFB_COMMAND   = XVFB + " " + DISPLAY_NUMBER_STRING + SCREEN_SIZE + " -dpi "; 
    private static int   baseDpi     = 100; 
    private Process    p; 

    public Display (int dpiFaktor) throws IOException, InterruptedException 
    { 
     checkExecutable(); 
     int dpi = baseDpi * dpiFaktor; 
     String cmd = XVFB_COMMAND + dpi; 
     p = Runtime.getRuntime().exec(cmd); 
     Thread.sleep(1000); 
     try 
     { 
      int exitValue = p.exitValue(); 
      String msgTemplate = "ERROR: Exit Value: %s. Display konnte nicht gestartet werden. Läuft ein Display noch auf %s ?"; 
      String msg = String.format(msgTemplate, exitValue, DISPLAY_NUMBER_STRING); 
      throw new IllegalStateException(msg); 
     } 
     catch (IllegalThreadStateException e) 
     { 
      // Das ist gut, der Prozess ist noch nicht beendet. 
      System.out.println("Switched on display at " + dpi + "dpi with command " + cmd); 
      return; 
     } 
    } 

    private void checkExecutable () 
    { 
     File file = new File(XVFB); 
     if (!file.canExecute()) 
     { 
      System.err.println("Xvfb is not installed at " + XVFB); 
      System.err.println("Install Xvfb by runing"); 
      System.err.println("apt-get install xvfb"); 
     } 
    } 

    public File captureScreenshot () throws IOException, InterruptedException 
    { 
     File tempFile = File.createTempFile("screenshots", ".png"); 
     String absolutePath = tempFile.getAbsolutePath(); 
     String cmd = "import -window root " + absolutePath; 
     String[] env = new String[] { "DISPLAY=" + DISPLAY_NUMBER_STRING }; 
     Process exec = Runtime.getRuntime().exec(cmd, env); 
     exec.waitFor(); 
     return tempFile; 
    } 

    public void shutdown () throws IOException, InterruptedException 
    { 
     p.destroy(); 
     try 
     { 
      Thread.sleep(1000); 
      int exitValue = p.exitValue(); 
      System.out.println("Display was switched off. ExitValue: " + exitValue); 
     } 
     catch (IllegalThreadStateException e) 
     { 
      // Das ist nicht gut, der Prozess sollte beendet sein. 
      // Kill it: 
      p = Runtime.getRuntime().exec("pkill Xvfb"); 
     } 
    } 

    public String getDisplayNumberString () 
    { 
     return DISPLAY_NUMBER_STRING; 
    } 

    public int getHeight () 
    { 
     return HEIGHT; 
    } 
} 

Und das ist mein pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>de.kicktipp</groupId> 
    <artifactId>screenshots</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <name>screenshots</name> 
    <properties> 
     <jdk.version>1.7</jdk.version> 
     <maven.version>3.0</maven.version> 
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
     <selenium-java.version>2.53.1</selenium-java.version> 
    </properties> 
    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.apache.maven.plugins</groupId> 
       <artifactId>maven-compiler-plugin</artifactId> 
       <configuration> 
        <source>${jdk.version}</source> 
        <target>${jdk.version}</target> 
       </configuration> 
      </plugin> 
     </plugins> 
    </build> 
    <dependencies> 
     <dependency> 
      <groupId>org.seleniumhq.selenium</groupId> 
      <artifactId>selenium-java</artifactId> 
      <version>${selenium-java.version}</version> 
     </dependency> 
    </dependencies> 
</project> 
Verwandte Themen