Take full page screenshot in Chrome with Selenium

前端 未结 2 2068
清歌不尽
清歌不尽 2020-11-27 15:21

I know this was not possible before, but now with the following update:

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

2条回答
  •  忘掉有多难
    2020-11-27 15:27

    Yes it possible to take a full page screenshot with Selenium since Chrome v59. The Chrome driver has two new endpoints to directly call the DevTools API:

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

    The Selenium API doesn't implement these commands, so you'll have to send them directly with the underlying executor. It's not straightforward, but at least it's possible to produce the exact same result as DevTools.

    Here's an example with python working on a local or remote instance:

    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)
    

    , and the code to take a full page screenshot :

    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'])
    

    With Java:

    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 getFullScreenshotAs(OutputType 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)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)response).get("result");
            return ((Map)result).get("value");
        }
    }
    

提交回复
热议问题