How to Screenshot a Website with Python: 3 Working Methods
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).
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.
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("YOUR_API_KEY")
image = client.screenshot(url="https://example.com")
with open("screenshot.png", "wb") as f:
f.write(image)
Full-Page with Options
from snaprender import SnapRender
client = SnapRender("YOUR_API_KEY")
image = client.screenshot(
url="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("YOUR_API_KEY")
image = client.screenshot(
url="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) |
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("YOUR_API_KEY")
def capture(url: str):
try:
image = client.screenshot(url=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 500 free screenshots per month with no credit card required. Get your API key in 30 seconds and start capturing.