How to Take a Screenshot of a Web Page with Selenium in Ruby
Use Selenium Ruby’s save_screenshot method to save a web page screenshot. This guide covers setup, viewport and element captures, full-page limits, and common fixes.
Use Selenium Ruby’s save_screenshot method after navigating to the page. By default it saves a PNG of the current browser viewport, not the whole document. Use a .png filename and put driver.quit in an ensure block so the browser closes even if navigation or capture fails.
1. Install Selenium and capture a page
Install the Ruby gem:
gem install selenium-webdriver
You also need a supported browser, such as Chrome, available to Selenium on the machine. Selenium’s current documentation shows Ruby driver creation, navigation, and screenshot capture in its WebDriver screenshot example.
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.get 'https://example.com/'
driver.save_screenshot('./screenshot.png')
ensure
driver.quit
end
Run it with ruby screenshot.rb. The path is relative to the process’s current working directory. Create its parent directory first if you use a nested path. The method writes PNG data; use a .png extension to match the file contents.
2. What the screenshot includes
Viewport capture
driver.save_screenshot('./screenshot.png') captures the current browsing context’s viewport: the visible browser page area at the time of the call. It does not automatically stitch together the page below the fold. The Ruby API documents the PNG output and a default full_page: false option in Selenium’s Ruby screenshot API reference.
Full-page capture
The Ruby API includes a full_page option, but full-page screenshots depend on support in the Selenium/browser/driver combination. Do not assume it works in every setup. Where supported by your installed version, try:
driver.save_screenshot('./full-page.png', full_page: true)
If the call raises an unsupported-operation error, your current combination does not support that operation. Check the API documentation matching your installed gem and browser, or use a browser-specific capture workflow. The screenshot module is marked private in the Ruby API reference and may change; treat this option as version-sensitive.
Element capture
To save one element rather than the viewport, find it and call save_screenshot on the element. The official Ruby example documents element capture; use .png because the API writes PNG data.
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.get 'https://example.com/'
heading = driver.find_element(:css, 'h1')
heading.save_screenshot('./heading.png')
ensure
driver.quit
end
See the Selenium element screenshot example. A missing selector raises an error, so wait for dynamic content when necessary.
3. Wait for the page before capturing
driver.get waits according to the browser’s page-load strategy, but client-side applications can continue rendering or fetching data afterward. For a page with a known ready element, wait for that element before capture:
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.get 'https://example.com/'
wait = Selenium::WebDriver::Wait.new(timeout: 15)
wait.until { driver.find_element(:css, 'main').displayed? }
driver.save_screenshot('./screenshot.png')
ensure
driver.quit
end
Choose a selector that indicates the content you need is present, not merely that the document shell loaded. Increase the timeout only when the page’s normal rendering time requires it. If an element is inside an iframe, switch into that frame before locating it; otherwise Selenium searches the current browsing context.
4. Options that affect the result
| Need | Approach | Limit or note |
|---|---|---|
| Visible page area | driver.save_screenshot('shot.png') |
Default is the current viewport. |
| Whole document | driver.save_screenshot('shot.png', full_page: true) |
Only when supported by the installed Selenium/browser combination. |
| One element | element.save_screenshot('element.png') |
Element must be found and capturable in the current context. |
| Different viewport dimensions | Set the browser window size before navigation or capture. | Viewport dimensions affect responsive layout and the captured area. |
| Wait for dynamic content | Use Selenium::WebDriver::Wait for a meaningful selector/state. |
A fixed sleep is less reliable because page load time varies. |
The screenshot API’s documented path argument is for a PNG file. If you need JPEG or WebP output, convert the PNG with an image-processing tool after capture; changing the filename extension does not change the encoded format.
5. Headless browser and repeatable runs
For automated jobs without a visible desktop, configure Chrome to run headlessly. Browser options vary by installed browser version; this example uses Selenium Ruby’s Chrome options:
require 'selenium-webdriver'
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless')
options.add_argument('--window-size=1440,1000')
driver = Selenium::WebDriver.for :chrome, options: options
begin
driver.get 'https://example.com/'
driver.save_screenshot('./screenshot.png')
ensure
driver.quit
end
Set a predictable window size when screenshots are compared across runs. Responsive breakpoints, device scale, browser fonts, animations, and page content can all change pixels. If visual consistency matters, use the same browser version, viewport, fonts, locale, and page state each time.
6. Or skip the browser setup
If you only need a page image and do not need Selenium to interact with the browser, ScreenshotNeo provides a screenshot API: ScreenshotNeo. A single GET request returns an image or PDF, and its parameter names are compatible with those used by other screenshot APIs. See the ScreenshotNeo API documentation.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Sign up for free screenshots.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Chrome or driver cannot start | Browser is missing, inaccessible, or incompatible with the Selenium setup. | Install a supported browser, check that the process can launch it, and consult Selenium setup documentation for the installed version. |
| Screenshot is blank or content is missing | Capture ran before client-side rendering or image loading finished. | Wait for the relevant element or state before calling save_screenshot. |
| Only the top of a long page appears | The default is a viewport screenshot. | Use full_page: true only if supported; otherwise use a suitable browser-specific method or a screenshot service with full-page capture. |
| Unsupported operation on full-page request | The driver/browser combination does not implement the option. | Check the API documentation for your gem version and omit the option or change capture approach. |
| Element not found | Selector is wrong, element has not rendered, or it is in another frame. | Verify the CSS selector, wait for the element, and switch to the correct frame when applicable. |
| Cannot write the screenshot | Parent directory does not exist or the process lacks write permission. | Use a writable path and create any needed directory before capture. |
| Image viewer rejects or misidentifies file | Extension does not match the PNG bytes. | Save with a .png extension or convert the image to the desired format. |
| Browser remains open after an error | Cleanup was skipped after an exception. | Place driver.quit in Ruby’s ensure block. |
8. Performance, reliability, and cost
With Selenium, each capture requires a browser session, navigation, page rendering, and file output. Reuse a session for a batch of pages when appropriate, but always close it after the work finishes. Browser startup and network/page rendering are often the larger sources of delay; waiting for a specific ready condition avoids arbitrary delays while reducing premature captures.
For reliable screenshots, control the viewport and browser version, use explicit waits, choose a writable output path, and clean up the driver in all execution paths. Pages may vary because of remote content, personalization, animations, or changing assets, so a screenshot is a record of the rendered state rather than a guarantee of identical output across runs.
Selenium’s software has no per-screenshot API charge in the workflow shown here, but you supply and operate the browser environment and its compute. ScreenshotNeo’s free tier includes 1,000 shots per month without a card; paid plans are Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan.
9. FAQ
Does Selenium Ruby save a screenshot as PNG?
Yes. The documented method writes PNG data. Give the file a .png extension.
Does save_screenshot capture the whole page?
By default it captures the viewport. Full-page capture is conditional on support in the installed browser and driver.
Can I take a screenshot of one HTML element?
Yes. Find the element and call save_screenshot on it, using a writable path ending in .png.
Can I use Selenium screenshots in a headless job?
Yes, if the browser is installed and configured for headless operation. Set the viewport explicitly for repeatable dimensions.


