Take Full Page Screenshot in Chrome with Selenium

Take screenshot of full page with Selenium Python with chromedriver

How it works: set browser height as longest as you can...

#coding=utf-8
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def test_fullpage_screenshot(self):
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--start-maximized')
driver = webdriver.Chrome(chrome_options=chrome_options)
driver.get("yoururlxxx")
time.sleep(2)

#the element with longest height on page
ele=driver.find_element("xpath", '//div[@class="react-grid-layout layout"]')
total_height = ele.size["height"]+1000

driver.set_window_size(1920, total_height) #the trick
time.sleep(2)
driver.save_screenshot("screenshot1.png")
driver.quit()

if __name__ == "__main__":
test_fullpage_screenshot()

Take full page screenshot in Chrome with Selenium

To do this with Selenium Webdriver in Java takes a bit of work.. As hinted by Florent B. we need to change some classes uses by the default ChromeDriver to make this work. First we need to make a new DriverCommandExecutor which adds the new Chrome commands:

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

After that we need to create a new ChromeDriver class which will then use this thing. We need to create the class because the original has no constructor that lets us replace the command executor... So the new class becomes:

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

This is mostly a copy of the original class, but with some constructors disabled (because some of the needed code is package private). If you are in need of these constructors you must place the classes in the package org.openqa.selenium.chrome.

With these changes you are able to call the required code, as shown by Florent B., but now in Java with the Selenium 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;
}
}

This lets you use the commands as specified, and creates a file with a png format image inside it. You can of course also directly create a BufferedImage by using ImageIO.read() on the bytes.

How to get screenshot of full webpage using Selenium and Java?

LE: I see quite a lot of people are interested in the full-page screenshot, so I thought I might update the answer with some positives (silver bullets).

There are quite a handful of web testing frameworks that can (with minimal setup & effort) produce a full-page screenshot. I come from a NodeJS testing environment, so I can only vouch the following: WebdriverIO & Google's Puppeteer.

If anyone is interested in an easy way to do it with WebdriverIO, check this answer.


Short answer is NO, YOU CANNOT, if you're only using Selenium (detailed reason bellow). But, what you can do is make the most out of your device's(monitor) viewport.

So, start your browser instance (driver) using ChromeOptions(), which is the method for setting ChromeDriver-specific capabilities. We can do the following:

  • maximize the window (using the --start-maximized switch);
  • go full-screen (F11-key on Windows, Control+Cmd+F on Mac, using the --start-fullscreen switch).
  • Note: complete list of Chrome command-line-switches can be found here.

Your code should look like this:

// Setting your Chrome options (Desired capabilities)
ChromeOptions options = new ChromeOptions();
options.add_argument('--start-maximized');
options.add_argument('--start-fullscreen');

// Creating a driver instance with the previous capabilities
WebDriver driver = new ChromeDriver(options);

// Load page & take screenshot of full-screen page
driver.get("http://google.com");
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);

Now regarding the full page problem, all drivers (chromedriver, geckodriver, IEDriver, EdgeDriver, etc.) are implementing the WebDriver W3C standard. Thus, they are dependent on the way the WebDriver team wants to expose the functionality of different features, in our case, 'Take Screenshot'.

If you read the definition, it clearly states the following:

The Take Screenshot command takes a screenshot of the top-level browsing context’s VIEWPORT.

Legacy-wise, some drivers were able to produce a full-page screenshot (read more about it here), like the old FirefoxDriver, IEDriver, etc. That is no longer the case as they now all implement (to the letter) the WebDriver W3C standard.

Take screenshot of full web page with Selenium Python with Chrome driver

Here is what I did to get the full-size screenshot of a webpage

from selenium import webdriver
browser = webdriver.Chrome()

# We go to the webpage here and wait 2 seconds for fully load
browser.get('https://someURL')
time.sleep(2)

# Where to save the picture
directory = '/home'
# Video title
title = 'Hello world'

try:
# We try to get the top-level component in which all out page is its children
# This is different for each website
elem = browser.find_element_by_id('main-layout-content')
# Get the height of the element, and adding some height just to be sage
total_height = elem.size['height'] + 1000
# Set the window size - what is the size of our screenshot
# The width is hardcoded because the screensize is fixed for each computer
browser.set_window_size(1920, total_height)
# Wait for 2 seconds
time.sleep(2)
# Take the screenshot
browser.find_element_by_id(
'main-layout-content').screenshot(f'./{directory}/{title}.png')
except SystemError as err:
print('Take screenshot error at' + title)

Selenium does not take the screenshot of the whole website, when its not headless

You can use Screenshot_Clipping in order to scroll the page and take screenshot from each scroll.

Just run this command in python3

pip install Selenium-Screenshot

Then create an object of Screenshot:

ob=Screenshot_Clipping.Screenshot()

With that object you can use ob.full_Screenshot in order to capture full screen

Checkout PythonFullPageScreenShot project on Github for full source code

Note that screenshot can take only 10000 of height of website, You can scale to 100 in order to capture full height

How can I take a screenshot with Selenium WebDriver?

Java

Yes, it is possible. The following example is in Java:

WebDriver driver = new FirefoxDriver();
driver.get("http://www.google.com/");
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
// Now you can do whatever you need to do with it, for example copy somewhere
FileUtils.copyFile(scrFile, new File("c:\\tmp\\screenshot.png"));


Related Topics



Leave a reply



Submit