How to properly take screenshots with Playwright and Selenium
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:
This popup will appear:
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:
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:
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"() => {{ {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"() => {{ {script} }}")
References
- Proxy Select Github
- Github issue on Puppetter that tipped me off: https://github.com/puppeteer/puppeteer/issues/1306