Creating Browser Screenshots on the Server Side
In the light of the plethora of gloomy headlines in the past few weeks I have started taking screenshots of some of the major web sites online, maybe just to have an archive of how the world ended one day :-)
I started thinking there’s got to be a way to automate this. Indeed, there are a number of downloadable utilities out there to create JPEG images of web sites, but I really wanted to build this myself. After some initial online research I decided to give it a spin in C#. I came across the System.Windows.Forms.WebBrowser class which, as it turns out, can do all these magic things for me directly on the server-side without requiring a desktop application.
The caveat is that the WebBrowser does need to execute in a standalone STA thread. As a side-note, the code is a total memory-hog the way it is written now, mostly due to IE.
Check out a single-threaded version of the code right here (full download at bottom of page):
using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.Threading; using System.Windows.Forms; namespace JToEE { public class Shoot { /// <summary> /// Set URL for the screenshot /// </summary> public string Url { get; set; } /// <summary> /// Output filename /// </summary> public string Filename { get; set; } /// <summary> /// Width of the browser window /// </summary> private int? Width { set; get; } /// <summary> /// Initial height of the browser (get re-sized /// automatically if the page is longer) /// </summary> private const int DefaultHeight = 768; /// <summary> /// Default width of the browser if not specified. This /// class doesn't re-size the width. /// </summary> private const int DefaultWidth = 1024; /// <summary> /// /// </summary> /// <param name="url"></param> /// <param name="filename"></param> public Shoot(string url, string filename) : this() { Url = url; Filename = filename; } /// <summary> /// /// </summary> public Shoot() { Width = 1024; } /// <summary> /// Main entry method which created the screenshot /// </summary> public void CreateScreenshot() { ThreadStart ts = Run; Thread t = new Thread(ts); t.SetApartmentState(ApartmentState.STA); t.Start(); t.Join(); } /// <summary> /// Does the work in the thread /// </summary> private void Run() { using(Bitmap bitmap = CreateBitmap()) { ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg); var myEncoder = System.Drawing.Imaging.Encoder.Quality; var myEncoderParameters = new EncoderParameters(1); // encode jpeg at 75% var myEncoderParameter = new EncoderParameter(myEncoder, 75L); myEncoderParameters.Param[0] = myEncoderParameter; // save to file bitmap.Save(Filename, jgpEncoder, myEncoderParameters); } } /// <summary> /// Get a hold of a jpeg encoder /// </summary> /// <param name="format"></param> /// <returns></returns> private static ImageCodecInfo GetEncoder(ImageFormat format) { ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); foreach (ImageCodecInfo codec in codecs) { if (codec.FormatID == format.Guid) { return codec; } } return null; } /// <summary> /// Create a bitmap of the browser window. The bitmap's height is /// automatically adapted to the length of the page while the width /// is kept at the specified value. /// </summary> /// <returns></returns> private Bitmap CreateBitmap() { using (var browser = new WebBrowser()) { browser.ScrollBarsEnabled = false; // set the initial height and width browser.Size = new Size(Width ?? 1024, DefaultHeight); browser.ScriptErrorsSuppressed = true; // supress creation of new windows browser.NewWindow += OnNewWindow; // navigate the browser to the URL browser.Navigate(Url); // wait for it to load WaitForBrowserReady(browser); // re-size the browser windows to the full height of the web page var finalHeight = browser.Document == null ? DefaultHeight : browser.Document.Body.ScrollRectangle.Height; // width of the bitmap var width = Width ?? DefaultWidth; // re-size the browser browser.Size = new Size(width, finalHeight); WaitForBrowserReady(browser); // create a bitmap Console.WriteLine(string.Format("Creating bitmap of size {0}x{1}", width, finalHeight)); var bitmap = new Bitmap(width, finalHeight); var rect = new Rectangle(0, 0, width, finalHeight); browser.DrawToBitmap(bitmap, rect); return bitmap; } } /// <summary> /// Waits for the browser to be ready loading the page /// </summary> /// <param name="browser"></param> private static void WaitForBrowserReady(WebBrowser browser) { Console.WriteLine("Waiting for browser.."); while (browser.ReadyState != WebBrowserReadyState.Complete) { Application.DoEvents(); } Console.WriteLine("Waiting ready"); } /// <summary> /// New window delegate -- basically a popup blocker. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void OnNewWindow(object sender, CancelEventArgs e) { e.Cancel = true; } } }
Download the full source code here.
Comments
Leave a Reply