Need to capture a website screenshot in Python? Whether you're building a web scraper, generating thumbnails, creating automated reports, or archiving pages, Python has several solid options.
This guide covers three approaches with working code you can copy-paste: Playwright (recommended for new projects), Selenium (if you're already using it), and a screenshot API (simplest, no browser management). If you want the API route straight away, the Python screenshot API quickstart gets you capturing in four lines.
Method 1: Playwright (Recommended)
Playwright is the modern choice for browser automation in Python. It's maintained by Microsoft, supports Chrome, Firefox, and Safari, and has a clean synchronous API that feels natural in Python. (For the full Playwright treatment including waiting strategies and the production pain list, see the Playwright screenshot guide.)
Installation
pip install playwright
playwright install chromium
The second command downloads a bundled Chromium binary (~150MB). This is a one-time download.
Basic Screenshot
from playwright.sync_api import sync_playwright
def take_screenshot(url: str, output_path: str = "screenshot.png"):
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url, wait_until="networkidle")
page.screenshot(path=output_path)
browser.close()
take_screenshot("https://example.com")
Full-Page Screenshot
Capture the entire scrollable page, not just the visible viewport:
from playwright.sync_api import sync_playwright
def full_page_screenshot(url: str, output_path: str = "fullpage.png"):
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url, wait_until="networkidle")
page.screenshot(path=output_path, full_page=True)
browser.close()
full_page_screenshot("https://example.com")
Custom Viewport Size
from playwright.sync_api import sync_playwright
def screenshot_with_size(url: str, width: int = 1920, height: int = 1080):
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": width, "height": height})
page.goto(url, wait_until="networkidle")
page.screenshot(path="screenshot.png")
browser.close()
screenshot_with_size("https://example.com", width=1440, height=900)
Mobile Device Emulation
from playwright.sync_api import sync_playwright
def mobile_screenshot(url: str, output_path: str = "mobile.png"):
with sync_playwright() as p:
browser = p.chromium.launch()
iphone = p.devices["iPhone 15 Pro"]
page = browser.new_page(**iphone)
page.goto(url, wait_until="networkidle")
page.screenshot(path=output_path)
browser.close()
mobile_screenshot("https://example.com")
Async Version
If you're using asyncio (FastAPI, aiohttp, etc.):
import asyncio
from playwright.async_api import async_playwright
async def take_screenshot_async(url: str, output_path: str = "screenshot.png"):
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto(url, wait_until="networkidle")
await page.screenshot(path=output_path)
await browser.close()
asyncio.run(take_screenshot_async("https://example.com"))
Generate PDF
from playwright.sync_api import sync_playwright
def page_to_pdf(url: str, output_path: str = "page.pdf"):
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url, wait_until="networkidle")
page.pdf(path=output_path, format="A4", print_background=True)
browser.close()
page_to_pdf("https://example.com")
Error Handling
Production-ready code with timeouts and error handling:
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout
def safe_screenshot(url: str, output_path: str = "screenshot.png") -> bool:
try:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_default_timeout(30000)
response = page.goto(url, wait_until="networkidle", timeout=30000)
if response and response.status >= 400:
print(f"Page returned HTTP {response.status}")
browser.close()
return False
page.screenshot(path=output_path)
browser.close()
return True
except PlaywrightTimeout:
print(f"Timeout loading {url}")
return False
except Exception as e:
print(f"Error: {e}")
return False
Method 2: Selenium
Selenium is the older browser automation tool. If you're already using it for testing or scraping, you can use it for screenshots too.
Installation
pip install selenium webdriver-manager
Basic Screenshot
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
def take_screenshot(url: str, output_path: str = "screenshot.png"):
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=options)
driver.get(url)
driver.save_screenshot(output_path)
driver.quit()
take_screenshot("https://example.com")
Full-Page Screenshot
Selenium doesn't have built-in full-page capture like Playwright. You need to resize the window to the page height:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
def full_page_screenshot(url: str, output_path: str = "fullpage.png"):
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=options)
driver.set_window_size(1920, 1080)
driver.get(url)
# Get the full page height
total_height = driver.execute_script("return document.body.scrollHeight")
driver.set_window_size(1920, total_height)
driver.save_screenshot(output_path)
driver.quit()
full_page_screenshot("https://example.com")
Selenium Limitations
- No native
wait_until="networkidle", so you need explicit waits or sleeps
- Full-page screenshots require manual window resizing
- WebDriver management adds complexity
- No built-in device emulation presets
- No WebP output format
For new projects, Playwright is the better choice. Use Selenium only if your project already depends on it.
Method 3: Screenshot API
If you don't want to install or manage a browser, a screenshot API handles everything server-side. You send a URL, it returns an image.
Installation
pip install snaprender
Basic Screenshot
from snaprender import SnapRender
client = SnapRender(api_key="YOUR_API_KEY")
image = client.capture("https://example.com")
with open("screenshot.png", "wb") as f:
f.write(image)
Full-Page with Options
from snaprender import SnapRender
client = SnapRender(api_key="YOUR_API_KEY")
image = client.capture(
"https://example.com",
format="webp",
width=1920,
height=1080,
full_page=True,
block_ads=True,
block_cookie_banners=True,
dark_mode=True
)
with open("screenshot.webp", "wb") as f:
f.write(image)
Mobile Screenshot
from snaprender import SnapRender
client = SnapRender(api_key="YOUR_API_KEY")
image = client.capture(
"https://example.com",
device="iphone_15_pro",
format="png"
)
with open("mobile.png", "wb") as f:
f.write(image)
Using requests Directly
If you prefer not to use the SDK:
import requests
response = requests.get(
"https://app.snap-render.com/v1/screenshot",
params={
"url": "https://example.com",
"format": "png",
"width": 1920,
"height": 1080
},
headers={"X-API-Key": "YOUR_API_KEY"}
)
if response.status_code == 200:
with open("screenshot.png", "wb") as f:
f.write(response.content)
else:
print(f"Error: {response.json()}")
Comparison
| Feature |
Playwright |
Selenium |
Screenshot API |
| Setup complexity |
Medium |
Medium |
Minimal |
| Browser download |
~150MB Chromium |
ChromeDriver |
None |
| Full-page capture |
Built-in |
Manual resize |
Built-in |
| Mobile emulation |
Built-in presets |
Manual config |
Built-in presets |
| Output formats |
PNG, JPEG, PDF |
PNG only |
PNG, JPEG, WebP, PDF |
| Ad blocking |
Manual implementation |
Manual implementation |
Built-in |
| Cookie banners |
Manual implementation |
Manual implementation |
Built-in |
| Async support |
Yes |
No |
Yes (via aiohttp) |
| Memory usage |
50-300MB per tab |
50-300MB per tab |
None (server-side) |
Every row above is one GET request away. 200 free renders a month, no card.
Start free
Batch Screenshots
Here's a practical example of screenshotting multiple URLs:
With Playwright (Async)
import asyncio
from playwright.async_api import async_playwright
async def batch_screenshots(urls: list[str]):
async with async_playwright() as p:
browser = await p.chromium.launch()
for url in urls:
page = await browser.new_page()
try:
await page.goto(url, wait_until="networkidle", timeout=30000)
filename = url.replace("https://", "").replace("/", "_") + ".png"
await page.screenshot(path=filename)
print(f"Captured: {url}")
except Exception as e:
print(f"Failed: {url}: {e}")
finally:
await page.close()
await browser.close()
urls = [
"https://github.com",
"https://stackoverflow.com",
"https://python.org"
]
asyncio.run(batch_screenshots(urls))
With Screenshot API
from snaprender import SnapRender
from concurrent.futures import ThreadPoolExecutor
client = SnapRender(api_key="YOUR_API_KEY")
def capture(url: str):
try:
image = client.capture(url, format="webp")
filename = url.replace("https://", "").replace("/", "_") + ".webp"
with open(filename, "wb") as f:
f.write(image)
print(f"Captured: {url}")
except Exception as e:
print(f"Failed: {url}: {e}")
urls = [
"https://github.com",
"https://stackoverflow.com",
"https://python.org"
]
with ThreadPoolExecutor(max_workers=5) as executor:
executor.map(capture, urls)
Which Method Should You Use?
Use Playwright if:
- You need full browser control (clicking, typing, scrolling before capture)
- You're building a scraping or testing pipeline
- You want to avoid third-party API dependencies
Use Selenium if:
- Your project already uses Selenium for other tasks
- You don't want to learn a new library
Use a Screenshot API if:
- You want the simplest code with the least maintenance
- You don't want to install or manage Chromium
- You need features like ad blocking and cookie banner removal out of the box
- You're deploying to environments where running a browser is impractical (serverless, shared hosting)
SnapRender offers 200 free screenshots per month with no credit card required. Get your API key in 30 seconds and start capturing.