<< Back to posts

How to properly take screenshots with Playwright and Selenium

Posted on January 2, 2024 • Tags: websites selenium playwright google chrome

In Python, to take a screenshot of a webpage using Selenium or Playwright, you do something like:

path_to_output: str = './screenshot.png'

# Selenium
selenium_driver: webdriver.Chrome = webdriver.Chrome()
selenium_driver.save_screenshot(path_to_output)

# Playwright
playwright_browser = sync_playwright().start().chromium.launch(channel="chrome")
playwright_page = playwright_browser.new_context().new_page()
playwright_page.screenshot(path=path_to_output)

However, this won’t actually include everything that’s visible on the webpage.

Problem: <select> menus disappear

One big issue is that this screenshot will not include any <select> menus in it.

That’s because the OS renders these select menus, not the web browser.

For example, if you click on the Actions <select> menu on the webpage below:

Screenshot 2024-01-02 at 9.28.40 PM

This popup will appear:

Screenshot 2024-01-02 at 9.28.36 PM

As you can tell, the styling is different than the rest of the webpage. That’s because Mac OS is rendering this menu, not the website.

Thus, neither Playwright nor Selenium will be able to capture this menu in their screenshot, since it isn’t “part of” the browser.

If you run the code above, your screenshot will look like this:

Screenshot 2024-01-02 at 9.28.40 PM

Solution: Rerender all <select> menus in HTML

The fix is simple – whenever you interact with the <select> menu, the webpage should render the menu in pure HTML and prevent the OS from doing so.

Luckily, someone has already done the hard work to make this happen – specifically, the Proxy Select repo.

You simply need to inject the following scripts into your webpage:

  1. This CSS script
  2. This JS script

Selenium

In Selenium, you can do this with the following code:

path_to_js: str = './path/to/proxy-select.js'
path_to_css: str = './path/to/proxy-select.css'

# Execute JS
with open(path_to_js, 'r') as fd:
  script: str = fd.read()
	selenium_driver.execute_script(script)

# Execute CSS
with open(path_to_css, 'r') as fd:
  script: str = f"""
  var style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = `{fd.read()}`;
  document.head.appendChild(style);
	"""
	selenium_driver.execute_script(script)

Playwright

And for Playwright:

path_to_js: str = './path/to/proxy-select.js'
path_to_css: str = './path/to/proxy-select.css'

# Execute JS
with open(path_to_js, 'r') as fd:
  script: str = fd.read()
	playwright_page.evaluate(f"() =&gt; {{ {script} }}")

# Execute CSS
with open(path_to_css, 'r') as fd:
  script: str = f"""
  var style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = `{fd.read()}`;
  document.head.appendChild(style);
	"""
	playwright_page.evaluate(f"() =&gt; {{ {script} }}")

References

  • Proxy Select Github
  • Github issue on Puppetter that tipped me off: https://github.com/puppeteer/puppeteer/issues/1306