Take screenshots of webpages or HTML files in .NET (C#)
Capturing screenshots of webpages programmatically is a valuable capability for automated testing, documentation, and monitoring. In this guide, we will explore how to capture screenshots of webpages and HTML files using .NET (C#).
Prerequisites
To follow along with this tutorial, you will need:
- .NET 8.0 or later
- Visual Studio 2022 or Visual Studio Code
- An installed Chrome browser
Using Selenium WebDriver
Selenium WebDriver is a powerful tool for browser automation that includes screenshot capabilities. First, install the required NuGet packages:
dotnet new console -n WebpageScreenshots
cd WebpageScreenshots
dotnet add package Selenium.WebDriver --version 4.28.0
dotnet add package Selenium.Support --version 4.28.0
Below is an example demonstrating how to capture the visible viewport using the Selenium 4
TakeScreenshot() extension method. It does not capture content below the viewport. Save the class
in ScreenshotCapture.cs and call ScreenshotCapture.CaptureWebpage(url, outputPath) from
Program.cs. Supply a .png output path whose parent directory exists.
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Support.Extensions;
public class ScreenshotCapture
{
public static void CaptureWebpage(string url, string outputPath)
{
try
{
var options = new ChromeOptions();
options.AddArgument("--headless=new");
using (var driver = new ChromeDriver(options))
{
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
driver.Navigate().GoToUrl(url);
// Wait for the document to finish loading
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => ((IJavaScriptExecutor)d)
.ExecuteScript("return document.readyState").Equals("complete"));
var screenshot = driver.TakeScreenshot();
screenshot.SaveAsFile(outputPath);
}
}
catch (WebDriverException ex)
{
throw new Exception($"Failed to capture screenshot: {ex.Message}", ex);
}
}
}
Browser compatibility
These examples use Chrome with --headless=new (Chrome 112 or later).
Selenium Manager, included with Selenium
4, locates or downloads a matching ChromeDriver when ChromeDriver starts. The first run may
require network access. Firefox and Edge require their respective driver and options classes.
Only render pages and HTML files you trust. Navigation and document.readyState do not validate
HTTP status codes or guarantee that an application's asynchronous content has finished loading.
Capturing local HTML files
To capture screenshots of local HTML files, use the file:// protocol with proper error handling.
Add the following method to ScreenshotCapture, with its using directives at the top of the file:
using System;
using System.IO;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Support.Extensions;
public static void CaptureLocalHtml(string htmlFilePath, string outputPath)
{
try
{
var absolutePath = Path.GetFullPath(htmlFilePath);
if (!File.Exists(absolutePath))
{
throw new FileNotFoundException("HTML file not found", absolutePath);
}
var fileUri = new Uri(absolutePath).AbsoluteUri;
var options = new ChromeOptions();
options.AddArgument("--headless=new");
using (var driver = new ChromeDriver(options))
{
driver.Navigate().GoToUrl(fileUri);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => ((IJavaScriptExecutor)d)
.ExecuteScript("return document.readyState").Equals("complete"));
var screenshot = driver.TakeScreenshot();
screenshot.SaveAsFile(outputPath);
}
}
catch (Exception ex)
{
throw new Exception($"Failed to capture local HTML: {ex.Message}", ex);
}
}
Handling dynamic content
Modern web applications often include dynamic content that requires additional time to load. Add
this method to the same class, replacing content-loaded with an element your application displays
when its content is ready:
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Support.Extensions;
public static void CaptureDynamicWebpage(string url, string outputPath)
{
try
{
var options = new ChromeOptions();
options.AddArgument("--headless=new");
using (var driver = new ChromeDriver(options))
{
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
driver.Navigate().GoToUrl(url);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
// Wait for a specific element to ensure dynamic content is loaded
wait.Until(d => d.FindElement(By.Id("content-loaded")).Displayed);
var screenshot = driver.TakeScreenshot();
screenshot.SaveAsFile(outputPath);
}
}
catch (WebDriverTimeoutException ex)
{
throw new Exception("Timeout waiting for dynamic content to load", ex);
}
}
Managing screenshot storage
Use an application-owned directory for this manager. It creates a screenshots subdirectory and
only cleans up files matching its own naming convention; other PNG files are left alone. Pass
screenshot.AsByteArray to SaveScreenshot:
using System;
using System.IO;
using System.Text.RegularExpressions;
public class ScreenshotManager
{
private readonly string _baseDirectory;
public ScreenshotManager(string baseDirectory)
{
_baseDirectory = Path.Combine(Path.GetFullPath(baseDirectory), "screenshots");
Directory.CreateDirectory(_baseDirectory);
}
public string SaveScreenshot(byte[] screenshotBytes, string prefix = "")
{
if (!Regex.IsMatch(prefix, @"\A[A-Za-z0-9_-]*\z"))
{
throw new ArgumentException("Use only letters, digits, underscores, or hyphens", nameof(prefix));
}
var fileName = $"{prefix}screenshot_{DateTime.UtcNow:yyyyMMdd_HHmmss}_{Guid.NewGuid():N}.png";
var filePath = Path.Combine(_baseDirectory, fileName);
using var output = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
output.Write(screenshotBytes);
return filePath;
}
public void CleanupOldScreenshots(int daysToKeep = 7)
{
if (daysToKeep < 1)
{
throw new ArgumentOutOfRangeException(nameof(daysToKeep));
}
var cutoffDate = DateTime.UtcNow.AddDays(-daysToKeep);
var files = Directory.GetFiles(_baseDirectory, "*screenshot_*.png");
foreach (var file in files)
{
if (Regex.IsMatch(Path.GetFileName(file),
@"\A[A-Za-z0-9_-]*screenshot_[0-9]{8}_[0-9]{6}_[0-9a-f]{32}\.png\z")
&& File.GetLastWriteTimeUtc(file) < cutoffDate)
{
File.Delete(file);
}
}
}
}
Handling different screen sizes
For responsive design testing, add this method to ScreenshotCapture to capture various browser
window sizes. Window dimensions include browser chrome, so the exact viewport may differ. This
checks responsive layouts without emulating a mobile device's user agent, touch input, or pixel
ratio:
using System;
using System.IO;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Support.Extensions;
public static void CaptureResponsiveScreenshots(string url, string outputDirectory)
{
try
{
Directory.CreateDirectory(outputDirectory);
var viewports = new[]
{
new { Width = 375, Height = 667, Name = "mobile" },
new { Width = 768, Height = 1024, Name = "tablet" },
new { Width = 1920, Height = 1080, Name = "desktop" }
};
var options = new ChromeOptions();
options.AddArgument("--headless=new");
using (var driver = new ChromeDriver(options))
{
foreach (var viewport in viewports)
{
driver.Manage().Window.Size = new System.Drawing.Size(viewport.Width, viewport.Height);
driver.Navigate().GoToUrl(url);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => ((IJavaScriptExecutor)d)
.ExecuteScript("return document.readyState").Equals("complete"));
var screenshot = driver.TakeScreenshot();
var outputPath = Path.Combine(outputDirectory,
$"{viewport.Name}_{Guid.NewGuid():N}.png");
screenshot.SaveAsFile(outputPath);
}
}
}
catch (Exception ex)
{
throw new Exception($"Failed to capture responsive screenshots: {ex.Message}", ex);
}
}
Conclusion
This guide demonstrates various approaches to capturing webpage screenshots programmatically in .NET, from static pages to dynamic content and responsive designs. The examples include detailed error handling, browser compatibility considerations, and best practices for managing screenshot storage.
