I came across an interesting problem while developing a monitoring tool recently. It was a simple HTTP check to see if a webpage had a certain piece of text on it. When it didn’t, I was saving the HTML and I had to sift through it at a later date to try and work out what went wrong. At some point, reading HTML (Or loading the HTML without any CSS/JS) become really tiresome so what I really wanted was the ability to take a screenshot of a webpage and save it as an image.
Boy. What a ride that took me on. After hours of searching, It felt like I had two options. Either I could use a dedicated paid library to save HTML to Image, or I could use many HTML to Image command line utilities – I would just have to invoke it from C# code. Neither really appealed a hell of a lot.
While explaining the problem to a friend, the conversation went a bit like this :
Friend : Do you need to manipulate the page in any way? Like click a link or something?
Me : No I just need to do a get request for a page and save the result, that’s it.
Friend : Oh OK. I was going to say, you could use the Selenium part for clicking around the page if you needed it. Anyway, good luck!
It took a few seconds to register but… Of course… Selenium can take screenshots of webpages! After all it’s just browser automation anyway! Admittedly it’s kinda heavy for just taking screenshots, but for my nice little utility I really didn’t care. And actually the ability to extend the code to be able to then *do* things on the page at a later date probably only makes it more of an attractive option.
Installing Selenium Into Your Project
We’re going to need two packages. The first is mandatory and that is the Selenium.Support package. Run the following from your package manager command line :
Next we want to install the driver for the browser we want to use. So this is going to totally depend on which browser you want to do the browsing. For me I want to use Chrome, but you can also choose to use Firefox or PhantomJS etc so modify the following command for what you need.
Our Selenium Screenshot Code
First I’ll give you the code, then we can talk it through.
ChromeOptions options = new ChromeOptions(); options.AddArgument("headless");//Comment if we want to see the window. var driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options); driver.Navigate().GoToUrl("https://www.google.com"); var screenshot = (driver as ITakesScreenshot).GetScreenshot(); screenshot.SaveAsFile("screenshot.png"); driver.Close(); driver.Quit();
Taking it step by step, it’s actually really easy (Almost too easy).
- Our options with Headless tells chrome that we don’t want to actually see the chrome window. Mostly because it’s annoying having it pop up all the time, but headless also sometimes causes problems with screenshots (More on this later).
- We create our ChomeDriver with a very specific path, this is because without it, we get this weird error which again, I’ll talk about shortly.
- Navigate to the page we want
- Take a screenshot
- Save it down as a file (We can also get a byte array at this point if we want to instead push it to cloud storage etc).
And that’s literally it. You can now take screenshots of webpages! But now onto the edge cases.
Unable To Find ChromeDriver Exception
So initially I had the following exception :
The chromedriver.exe file does not exist in the current directory or in a directory on the PATH environment variable.
It turned out to be a two pronged fix.
The first is that we need to make sure we have installed the actual ChromeDriver nuget package *and* rebuilt our solution completely. The nuget package command is :
This is actually a little confusing because the ChromeDriver class is available in code, intellisense and all, but it won’t run unless you install that package.
Next for some reason it couldn’t find the ChromeDriver.exe in my applications bin folder still. What seemed to fix it was the fact you can give it a bit of a nudge in the right direction, and so telling it to look in the current application folder (e.g. the Bin folder) got us there.
var driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options);
Screenshot Is Off Screen
While testing this out I noticed that when I swapped to using the headless chrome browser, that sometimes what I wanted to view was just off screen. While there was actually some nifty libraries to take just a screenshot of a single element in Selenium, I had a cruder (but simpler) solution!
If we can just make the window a reasonable width, but really long, then we can take a screenshot of the “browser window”, and it should capture everything. Obviously this isn’t an exact science and if the length of your page is forever changing, this may not work. But if it’s static content, just a little longer, this works perfectly.
The code looks a bit like :
var driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options); driver.Manage().Window.Size= new System.Drawing.Size(1500, 2000);
So now as long as our content is no longer than 2000px long (In my case, it will never be), then we are set. Again, there’s probably more elegant solutions but this works so well for what I need it to do.