2017-07-19 26 views
7

मैं जानता हूँ कि यह संभव नहीं था before साथ क्रोम में पूरे पृष्ठ का स्क्रीन शॉट ले लो, लेकिन अब निम्न अद्यतन के साथ:सेलेनियम

https://developers.google.com/web/updates/2017/04/devtools-release-notes#screenshots

इस क्रोम देव उपकरणों का उपयोग संभव हो रहा है।

क्या अब जावा में सेलेनियम का उपयोग करना संभव है?

+0

AFAIK इस 'selenium' +' PhantomJS' – Andersson

+2

हाँ में संभव है, लेकिन आप जावा उपयोग कर रहे हैं मैं क्रोम ड्राइवर –

+0

की जरूरत है, ऐसा लगता है कि किसी को आप के लिए बात की इस तरह करने के लिए एक अच्छा पुस्तकालय बनाया गया है। हो सकता है कि यह कोशिश करें https://github.com/yandex-qatools/ashot/ – stewartm

उत्तर

2

ऐसा करने के लिए जावा में सेलेनियम वेबड्राइवर के साथ थोड़ा सा काम लगता है .. जैसा कि फ्लोरेंट बी द्वारा संकेत दिया गया है, हमें इस काम को करने के लिए डिफ़ॉल्ट क्रोमड्राइवर द्वारा कुछ कक्षाओं को उपयोग करने की आवश्यकता है।

import com.google.common.collect.ImmutableMap; 
import org.openqa.selenium.remote.CommandInfo; 
import org.openqa.selenium.remote.http.HttpMethod; 
import org.openqa.selenium.remote.service.DriverCommandExecutor; 
import org.openqa.selenium.remote.service.DriverService; 

public class MyChromeDriverCommandExecutor extends DriverCommandExecutor { 
    private static final ImmutableMap<String, CommandInfo> CHROME_COMMAND_NAME_TO_URL; 

    public MyChromeDriverCommandExecutor(DriverService service) { 
     super(service, CHROME_COMMAND_NAME_TO_URL); 
    } 

    static { 
     CHROME_COMMAND_NAME_TO_URL = ImmutableMap.of("launchApp", new CommandInfo("/session/:sessionId/chromium/launch_app", HttpMethod.POST) 
     , "sendCommandWithResult", new CommandInfo("/session/:sessionId/chromium/send_command_and_get_result", HttpMethod.POST) 
     ); 
    } 
} 

कि हम एक नई ChromeDriver वर्ग जो तब इस बात का उपयोग करेगा बनाने की आवश्यकता के बाद: सबसे पहले हम एक नए DriverCommandExecutor जो कहते हैं नई क्रोम आदेश बनाने की जरूरत है। हम क्योंकि मूल कोई निर्माता हमें आदेश निष्पादक की जगह देता है कि है वर्ग बनाने की आवश्यकता ... तो नया वर्ग हो जाता है:

import com.google.common.collect.ImmutableMap; 
import org.openqa.selenium.Capabilities; 
import org.openqa.selenium.WebDriverException; 
import org.openqa.selenium.chrome.ChromeDriverService; 
import org.openqa.selenium.html5.LocalStorage; 
import org.openqa.selenium.html5.Location; 
import org.openqa.selenium.html5.LocationContext; 
import org.openqa.selenium.html5.SessionStorage; 
import org.openqa.selenium.html5.WebStorage; 
import org.openqa.selenium.interactions.HasTouchScreen; 
import org.openqa.selenium.interactions.TouchScreen; 
import org.openqa.selenium.mobile.NetworkConnection; 
import org.openqa.selenium.remote.FileDetector; 
import org.openqa.selenium.remote.RemoteTouchScreen; 
import org.openqa.selenium.remote.RemoteWebDriver; 
import org.openqa.selenium.remote.html5.RemoteLocationContext; 
import org.openqa.selenium.remote.html5.RemoteWebStorage; 
import org.openqa.selenium.remote.mobile.RemoteNetworkConnection; 

public class MyChromeDriver extends RemoteWebDriver implements LocationContext, WebStorage, HasTouchScreen, NetworkConnection { 
    private RemoteLocationContext locationContext; 
    private RemoteWebStorage webStorage; 
    private TouchScreen touchScreen; 
    private RemoteNetworkConnection networkConnection; 

    //public MyChromeDriver() { 
    // this(ChromeDriverService.createDefaultService(), new ChromeOptions()); 
    //} 
    // 
    //public MyChromeDriver(ChromeDriverService service) { 
    // this(service, new ChromeOptions()); 
    //} 

    public MyChromeDriver(Capabilities capabilities) { 
     this(ChromeDriverService.createDefaultService(), capabilities); 
    } 

    //public MyChromeDriver(ChromeOptions options) { 
    // this(ChromeDriverService.createDefaultService(), options); 
    //} 

    public MyChromeDriver(ChromeDriverService service, Capabilities capabilities) { 
     super(new MyChromeDriverCommandExecutor(service), capabilities); 
     this.locationContext = new RemoteLocationContext(this.getExecuteMethod()); 
     this.webStorage = new RemoteWebStorage(this.getExecuteMethod()); 
     this.touchScreen = new RemoteTouchScreen(this.getExecuteMethod()); 
     this.networkConnection = new RemoteNetworkConnection(this.getExecuteMethod()); 
    } 

    @Override 
    public void setFileDetector(FileDetector detector) { 
     throw new WebDriverException("Setting the file detector only works on remote webdriver instances obtained via RemoteWebDriver"); 
    } 

    @Override 
    public LocalStorage getLocalStorage() { 
     return this.webStorage.getLocalStorage(); 
    } 

    @Override 
    public SessionStorage getSessionStorage() { 
     return this.webStorage.getSessionStorage(); 
    } 

    @Override 
    public Location location() { 
     return this.locationContext.location(); 
    } 

    @Override 
    public void setLocation(Location location) { 
     this.locationContext.setLocation(location); 
    } 

    @Override 
    public TouchScreen getTouch() { 
     return this.touchScreen; 
    } 

    @Override 
    public ConnectionType getNetworkConnection() { 
     return this.networkConnection.getNetworkConnection(); 
    } 

    @Override 
    public ConnectionType setNetworkConnection(ConnectionType type) { 
     return this.networkConnection.setNetworkConnection(type); 
    } 

    public void launchApp(String id) { 
     this.execute("launchApp", ImmutableMap.of("id", id)); 
    } 
} 

यह ज्यादातर मूल वर्ग की एक प्रति है, लेकिन कुछ निर्माताओं विकलांग के साथ (क्योंकि कुछ आवश्यक कोड पैकेज निजी है)। यदि आपको इन रचनाकारों की आवश्यकता है तो आपको कक्षाओं को org.openqa.selenium.chrome पैकेज में रखना होगा।

इन परिवर्तनों को आप आवश्यक कोड फोन करने में सक्षम हैं के साथ

, के रूप में फ्लोरेंट बी द्वारा दिखाया गया है, लेकिन अब जावा में सेलेनियम API के साथ:

import com.google.common.collect.ImmutableMap; 
import org.openqa.selenium.remote.Command; 
import org.openqa.selenium.remote.Response; 

import javax.annotation.Nonnull; 
import javax.annotation.Nullable; 
import javax.imageio.ImageIO; 
import java.awt.image.BufferedImage; 
import java.io.ByteArrayInputStream; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.util.Base64; 
import java.util.HashMap; 
import java.util.Map; 

public class ChromeExtender { 
    @Nonnull 
    private MyChromeDriver m_wd; 

    public ChromeExtender(@Nonnull MyChromeDriver wd) { 
     m_wd = wd; 
    } 

    public void takeScreenshot(@Nonnull File output) throws Exception { 
     Object visibleSize = evaluate("({x:0,y:0,width:window.innerWidth,height:window.innerHeight})"); 
     Long visibleW = jsonValue(visibleSize, "result.value.width", Long.class); 
     Long visibleH = jsonValue(visibleSize, "result.value.height", Long.class); 

     Object contentSize = send("Page.getLayoutMetrics", new HashMap<>()); 
     Long cw = jsonValue(contentSize, "contentSize.width", Long.class); 
     Long ch = jsonValue(contentSize, "contentSize.height", Long.class); 

     /* 
     * In chrome 61, delivered one day after I wrote this comment, the method forceViewport was removed. 
     * I commented it out here with the if(false), and hopefully wrote a working alternative in the else 8-/ 
     */ 
     if(false) { 
      send("Emulation.setVisibleSize", ImmutableMap.of("width", cw, "height", ch)); 
      send("Emulation.forceViewport", ImmutableMap.of("x", Long.valueOf(0), "y", Long.valueOf(0), "scale", Long.valueOf(1))); 
     } else { 
      send("Emulation.setDeviceMetricsOverride", 
       ImmutableMap.of("width", cw, "height", ch, "deviceScaleFactor", Long.valueOf(1), "mobile", Boolean.FALSE, "fitWindow", Boolean.FALSE) 
      ); 
      send("Emulation.setVisibleSize", ImmutableMap.of("width", cw, "height", ch)); 
     } 

     Object value = send("Page.captureScreenshot", ImmutableMap.of("format", "png", "fromSurface", Boolean.TRUE)); 

     // Since chrome 61 this call has disappeared too; it does not seem to be necessary anymore with the new code. 
     // send("Emulation.resetViewport", ImmutableMap.of()); 
     send("Emulation.setVisibleSize", ImmutableMap.of("x", Long.valueOf(0), "y", Long.valueOf(0), "width", visibleW, "height", visibleH)); 

     String image = jsonValue(value, "data", String.class); 
     byte[] bytes = Base64.getDecoder().decode(image); 

     try(FileOutputStream fos = new FileOutputStream(output)) { 
      fos.write(bytes); 
     } 
    } 

    @Nonnull 
    private Object evaluate(@Nonnull String script) throws IOException { 
     Map<String, Object> param = new HashMap<>(); 
     param.put("returnByValue", Boolean.TRUE); 
     param.put("expression", script); 

     return send("Runtime.evaluate", param); 
    } 

    @Nonnull 
    private Object send(@Nonnull String cmd, @Nonnull Map<String, Object> params) throws IOException { 
     Map<String, Object> exe = ImmutableMap.of("cmd", cmd, "params", params); 
     Command xc = new Command(m_wd.getSessionId(), "sendCommandWithResult", exe); 
     Response response = m_wd.getCommandExecutor().execute(xc); 

     Object value = response.getValue(); 
     if(response.getStatus() == null || response.getStatus().intValue() != 0) { 
      //System.out.println("resp: " + response); 
      throw new MyChromeDriverException("Command '" + cmd + "' failed: " + value); 
     } 
     if(null == value) 
      throw new MyChromeDriverException("Null response value to command '" + cmd + "'"); 
     //System.out.println("resp: " + value); 
     return value; 
    } 

    @Nullable 
    static private <T> T jsonValue(@Nonnull Object map, @Nonnull String path, @Nonnull Class<T> type) { 
     String[] segs = path.split("\\."); 
     Object current = map; 
     for(String name: segs) { 
      Map<String, Object> cm = (Map<String, Object>) current; 
      Object o = cm.get(name); 
      if(null == o) 
       return null; 
      current = o; 
     } 
     return (T) current; 
    } 
} 

यह आप आदेशों को निर्दिष्ट के रूप में उपयोग करने देता है, और बनाता है एक फ़ाइल जिसमें एक पीएनजी प्रारूप छवि है। बाइट्स पर ImageIO.read() का उपयोग करके आप निश्चित रूप से सीधे BufferedImage भी बना सकते हैं।

+0

महान काम !!! ChromeExtender क्लास के लिए आयात जोड़ने का कोई मौका? मैंने उनमें से कुछ पर अनुमान लगाया। – SiKing

+0

हाय @ सिकिंग, मैंने आयात जोड़ा। लेकिन ध्यान रखें, क्रोम 61, जो मुझे आज मिला, मेरे मूल उदाहरण में उपयोग किए गए बलव्यूपोर्ट कॉल को हटा देता है। मैंने एक विकल्प जोड़ा है जो लेने के लिए कोड में मेरे लिए काम करता हैस्क्रीनशॉट()। – fjalvingh

+0

Emulation.resetViewport "क्रोम 61 पर काम नहीं कर रहा है (क्रोम 60 पर ठीक था)। क्या कोई विकल्प है? –

10

हाँ क्रोम v59 के बाद सेलेनियम के साथ एक पूर्ण पृष्ठ स्क्रीनशॉट लेना संभव है।

/session/:sessionId/chromium/send_command_and_get_result 
/session/:sessionId/chromium/send_command 

सेलेनियम एपीआई इन आदेशों को लागू नहीं करता है, ताकि आप उन्हें अंतर्निहित निष्पादक के साथ सीधे भेजने के लिए होगा: क्रोम चालक दो नए अंतिमबिंदुओं सीधे DevTools API कॉल करने के लिए है। यह सीधा नहीं है, लेकिन कम से कम DevTools के समान परिणाम उत्पन्न करना संभव है।

यहाँ अजगर एक स्थानीय या दूरदराज के उदाहरण पर काम कर के साथ एक उदाहरण है:

from selenium import webdriver 
import json, base64 

capabilities = { 
    'browserName': 'chrome', 
    'chromeOptions': { 
    'useAutomationExtension': False, 
    'args': ['--disable-infobars'] 
    } 
} 

driver = webdriver.Chrome(desired_capabilities=capabilities) 
driver.get("https://stackoverflow.com/questions") 

png = chrome_takeFullScreenshot(driver) 

with open(r"C:\downloads\screenshot.png", 'wb') as f: 
    f.write(png) 

, और कोड एक पूरा पृष्ठ स्क्रीनशॉट लेने के लिए:

def chrome_takeFullScreenshot(driver) : 

    def send(cmd, params): 
    resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id 
    url = driver.command_executor._url + resource 
    body = json.dumps({'cmd':cmd, 'params': params}) 
    response = driver.command_executor._request('POST', url, body) 
    return response.get('value') 

    def evaluate(script): 
    response = send('Runtime.evaluate', {'returnByValue': True, 'expression': script}) 
    return response['result']['value'] 

    metrics = evaluate(\ 
    "({" + \ 
     "width: Math.max(window.innerWidth, document.body.scrollWidth, document.documentElement.scrollWidth)|0," + \ 
     "height: Math.max(innerHeight, document.body.scrollHeight, document.documentElement.scrollHeight)|0," + \ 
     "deviceScaleFactor: window.devicePixelRatio || 1," + \ 
     "mobile: typeof window.orientation !== 'undefined'" + \ 
    "})") 
    send('Emulation.setDeviceMetricsOverride', metrics) 
    screenshot = send('Page.captureScreenshot', {'format': 'png', 'fromSurface': True}) 
    send('Emulation.clearDeviceMetricsOverride', {}) 

    return base64.b64decode(screenshot['data']) 
जावा के साथ

:

public static void main(String[] args) throws Exception { 

    ChromeOptions options = new ChromeOptions(); 
    options.setExperimentalOption("useAutomationExtension", false); 
    options.addArguments("disable-infobars"); 

    ChromeDriverEx driver = new ChromeDriverEx(options); 

    driver.get("https://stackoverflow.com/questions"); 
    File file = driver.getFullScreenshotAs(OutputType.FILE); 
} 
import java.lang.reflect.Method; 
import java.util.Map; 
import com.google.common.collect.ImmutableMap; 
import org.openqa.selenium.OutputType; 
import org.openqa.selenium.chrome.ChromeDriver; 
import org.openqa.selenium.chrome.ChromeDriverService; 
import org.openqa.selenium.chrome.ChromeOptions; 
import org.openqa.selenium.remote.CommandInfo; 
import org.openqa.selenium.remote.HttpCommandExecutor; 
import org.openqa.selenium.remote.http.HttpMethod; 


public class ChromeDriverEx extends ChromeDriver { 

    public ChromeDriverEx() throws Exception { 
     this(new ChromeOptions()); 
    } 

    public ChromeDriverEx(ChromeOptions options) throws Exception { 
     this(ChromeDriverService.createDefaultService(), options); 
    } 

    public ChromeDriverEx(ChromeDriverService service, ChromeOptions options) throws Exception { 
     super(service, options); 
     CommandInfo cmd = new CommandInfo("/session/:sessionId/chromium/send_command_and_get_result", HttpMethod.POST); 
     Method defineCommand = HttpCommandExecutor.class.getDeclaredMethod("defineCommand", String.class, CommandInfo.class); 
     defineCommand.setAccessible(true); 
     defineCommand.invoke(super.getCommandExecutor(), "sendCommand", cmd); 
    } 

    public <X> X getFullScreenshotAs(OutputType<X> outputType) throws Exception { 
     Object metrics = sendEvaluate(
      "({" + 
      "width: Math.max(window.innerWidth,document.body.scrollWidth,document.documentElement.scrollWidth)|0," + 
      "height: Math.max(window.innerHeight,document.body.scrollHeight,document.documentElement.scrollHeight)|0," + 
      "deviceScaleFactor: window.devicePixelRatio || 1," + 
      "mobile: typeof window.orientation !== 'undefined'" + 
      "})"); 
     sendCommand("Emulation.setDeviceMetricsOverride", metrics); 
     Object result = sendCommand("Page.captureScreenshot", ImmutableMap.of("format", "png", "fromSurface", true)); 
     sendCommand("Emulation.clearDeviceMetricsOverride", ImmutableMap.of()); 
     String base64EncodedPng = (String)((Map<String, ?>)result).get("data"); 
     return outputType.convertFromBase64Png(base64EncodedPng); 
    } 

    protected Object sendCommand(String cmd, Object params) { 
     return execute("sendCommand", ImmutableMap.of("cmd", cmd, "params", params)).getValue(); 
    } 

    protected Object sendEvaluate(String script) { 
     Object response = sendCommand("Runtime.evaluate", ImmutableMap.of("returnByValue", true, "expression", script)); 
     Object result = ((Map<String, ?>)response).get("result"); 
     return ((Map<String, ?>)result).get("value"); 
    } 
} 
+1

जावा में ऐसा करने के बारे में कोई संकेत? –

+2

'HttpCommandExecutor' को बढ़ाने का प्रयास करें और नए एंडपॉइंट का उपयोग करने के लिए' DefineCommand' पर कॉल करें। फिर विस्तारित 'HttpCommandExecutor' के साथ एक नया रिमोट वेब ड्राइवर तुरंत चालू करें। –

+1

https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/chrome/ChromeDriverCommandExecutor.java –