Capturas de pantalla de páginas web o archivos HTML en .NET (C#)
Tomar capturas de pantalla de páginas web mediante programación es una capacidad valiosa para las pruebas automatizadas, la documentación y el monitoreo. En esta guía, exploraremos cómo tomar capturas de pantalla de páginas web y archivos HTML con .NET (C#).
Requisitos previos
Para seguir este tutorial, necesitarás:
- .NET 8.0 o posterior
- Visual Studio 2022 o Visual Studio Code
- El navegador Chrome instalado
Uso de Selenium WebDriver
Selenium WebDriver es una herramienta potente para la automatización de navegadores que incluye funciones de captura de pantalla. Primero, instala los paquetes NuGet necesarios:
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
A continuación se muestra un ejemplo que demuestra cómo capturar el área visible (viewport) con el
método de extensión TakeScreenshot() de Selenium 4. No captura el contenido que queda
por debajo del área visible. Guarda la clase en ScreenshotCapture.cs y llama a
ScreenshotCapture.CaptureWebpage(url, outputPath) desde Program.cs. Proporciona una ruta de salida
.png cuyo directorio padre exista.
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);
}
}
}
Compatibilidad con navegadores
Estos ejemplos usan Chrome con --headless=new (Chrome 112 o posterior).
Selenium Manager, incluido en Selenium 4, localiza o descarga un
ChromeDriver compatible cuando se inicia ChromeDriver. La primera ejecución puede
requerir acceso a la red. Firefox y Edge requieren sus respectivas clases de controlador y de
opciones.
Renderiza únicamente páginas y archivos HTML en los que confíes. La navegación y
document.readyState no validan los códigos de estado HTTP ni garantizan que el contenido
asíncrono de una aplicación haya terminado de cargarse.
Captura de archivos HTML locales
Para tomar capturas de pantalla de archivos HTML locales, usa el protocolo
file:// con un manejo de errores adecuado. Agrega el siguiente método a
ScreenshotCapture, con sus directivas using al inicio del
archivo:
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);
}
}
Manejo de contenido dinámico
Las aplicaciones web modernas suelen incluir contenido dinámico que necesita tiempo adicional para
cargarse. Agrega este método a la misma clase, reemplazando content-loaded por un
elemento que tu aplicación muestre cuando su contenido esté listo:
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);
}
}
Gestión del almacenamiento de capturas de pantalla
Usa un directorio propiedad de la aplicación para este gestor. Crea un subdirectorio
screenshots y solo limpia los archivos que coinciden con su propia convención de
nombres; los demás archivos PNG quedan intactos. Pasa screenshot.AsByteArray a
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);
}
}
}
}
Manejo de distintos tamaños de pantalla
Para probar el diseño adaptable, agrega este método a ScreenshotCapture para capturar
distintos tamaños de ventana del navegador. Las dimensiones de la ventana incluyen la interfaz del
navegador, por lo que el área visible exacta puede variar. Esto comprueba los diseños adaptables sin
emular el user agent, la entrada táctil ni la relación de píxeles de un dispositivo móvil:
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);
}
}
Conclusión
Esta guía muestra varios enfoques para tomar capturas de pantalla de páginas web mediante programación en .NET, desde páginas estáticas hasta contenido dinámico y diseños adaptables. Los ejemplos incluyen un manejo detallado de errores, consideraciones de compatibilidad con navegadores y buenas prácticas para gestionar el almacenamiento de capturas de pantalla.
