5

Voglio prendere alcuni screenshot della mia app ibrida per collegare itunes automaticamente. Sto usando Ubuntu 14.04. chromedriver 2.15.322448Come prendere schermate Retina con Xvfb e selenio

Prendere schermate automaticamente è facile con Selenium e Xvfb. Ma non è facile ottenere schermate della retina.

ho iniziato la mia Xvfb con un dpi più elevata:

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

Quando controllo il tutto informazioni sul display sembra essere di destra:

xdpyinfo -display :99 

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

poi comincio il mio chromedriver come questo

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

e dopo qualche altro noioso codice, prendo lo screenshot:

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

Questo non funziona. Lo screenshot è in dpi regolari. Quindi l'immagine del sito web catturata è solo 320x460 e non 640x960 come dovrebbe essere.

Ho impostato un punto di interruzione poco prima che lo screenshot è stato preso e fatto uscire il framebuffer in questo modo:

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

Result of xwd dumping the content of the virtual framebuffer

Come si può vedere la barra del titolo è reso in relazione ad una maggiore dpi, ma il resto della finestra del browser no.

Quindi, come posso eseguire un chromedriver con più dpi per acquisire schermate retina? È possibile?

+0

hai trovato una soluzione a questo?Sono bloccato esattamente allo stesso problema, lo apprezzerei se avessi condiviso la tua esperienza con gli screenshot della risoluzione xvfb e retina – Alex

+1

Ho postato una risposta qui sotto. Spero possa essere d'aiuto. – Janning

risposta

1

Se si vuole solo prendere alcune screenhosts è possibile utilizzare Google Chrome Software senza testa. Ad esempio ottenere uno screenshot di retina è facile come

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

Sono di fronte allo stesso problema e ancora bloccato, ma i seguenti potrebbero essere utili. Mi ha permesso di escludere xvfb o chrome collegando una connessione VNC al framebuffer xvfb.

#!/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 

Dopo VNC'ing in, google-chrome GUI può essere caricata dal terminale. La navigazione verso le pagine Web conferma che Chrome sta visualizzando le pagine con il DPI corretto. Screenshot http://i.stack.imgur.com/iEjo0.jpg

Mi piacerebbe molto farlo funzionare anche in questo caso, si prega di contattare se si dispone di nuovi sviluppi. Ho usato https://registry.hub.docker.com/u/selenium/standalone-chrome-debug/ BTW.

0

Sono passato a Firefox e ha funzionato con il seguente codice. Ma al momento non funziona come il selenio non funziona bene con la mia versione 47 di Firefox, vedi https://github.com/SeleniumHQ/selenium/issues/2257 Quindi non posso testare questo codice in questo momento, ma l'ultima volta sono riuscito a ottenere schermate con retina:

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

E questo è il mio 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>