Zendriver makes it easy to capture web pages and elements as screenshots and PDF documents.
Taking screenshots
Full page screenshots
Capture the entire page or just the viewport:
import asyncio
import zendriver as zd
async def main ():
browser = await zd.start()
tab = await browser.get( "https://example.com" )
# Screenshot current viewport (visible area)
await tab.save_screenshot( "viewport.jpg" )
# Screenshot entire page (full_page=True)
await tab.save_screenshot( "fullpage.jpg" , full_page = True )
await browser.stop()
if __name__ == "__main__" :
asyncio.run(main())
Change format from JPEG to PNG:
# Save as PNG with transparency support
await tab.save_screenshot( "screenshot.png" , format = "png" )
# Full page PNG
await tab.save_screenshot(
"fullpage.png" ,
format = "png" ,
full_page = True
)
Auto-generated filenames
Let Zendriver generate filenames based on the URL and timestamp:
# Filename: example.com__page_2026-03-01_14-30-45.jpg
filepath = await tab.save_screenshot()
print ( f "Saved to: { filepath } " )
# Or explicitly use "auto"
filepath = await tab.save_screenshot( filename = "auto" , format = "png" )
Auto-generated filenames include the hostname, path, and timestamp to avoid collisions.
Element screenshots
Capture specific elements instead of the entire page:
# Screenshot a single element
header = await tab.select( "header" )
await header.save_screenshot( "header.jpg" )
# Screenshot with custom scale
logo = await tab.select( ".logo" )
await logo.save_screenshot(
"logo.png" ,
format = "png" ,
scale = 2.0 # 2x size
)
# Half scale
await logo.save_screenshot( "logo-small.jpg" , scale = 0.5 )
Element screenshots will raise RuntimeError if the element is hidden, has no size, or is not visible in the viewport.
Base64 encoded screenshots
Get screenshot data as base64 string instead of saving to disk:
# Page screenshot as base64
data = await tab.screenshot_b64( format = "jpeg" )
print ( f "Data length: { len (data) } characters" )
# Use the base64 data
import base64
image_bytes = base64.b64decode(data)
# Or send via API
import requests
requests.post(
"https://api.example.com/upload" ,
json = { "image" : data}
)
# Element screenshot as base64
element = await tab.select( ".chart" )
data = await element.screenshot_b64( format = "png" )
Creating PDFs
Generate PDF documents from web pages:
import asyncio
import zendriver as zd
async def main ():
browser = await zd.start()
tab = await browser.get( "https://example.com/article" )
# Save as PDF
await tab.print_to_pdf( "article.pdf" )
await browser.stop()
if __name__ == "__main__" :
asyncio.run(main())
PDF options
Customize PDF output with options from Chrome DevTools Protocol:
from zendriver import cdp
# A4 landscape with margins
await tab.print_to_pdf(
"output.pdf" ,
landscape = True ,
paper_width = 8.27 , # A4 width in inches
paper_height = 11.69 , # A4 height in inches
margin_top = 0.4 ,
margin_bottom = 0.4 ,
margin_left = 0.4 ,
margin_right = 0.4 ,
print_background = True
)
# Custom page ranges
await tab.print_to_pdf(
"pages.pdf" ,
page_ranges = "1-5,8,11-13"
)
Common PDF options:
landscape - Paper orientation (default: False)
print_background - Include background graphics (default: False)
scale - Scale of the PDF (default: 1.0)
paper_width - Paper width in inches
paper_height - Paper height in inches
margin_top/bottom/left/right - Margins in inches
page_ranges - Page ranges to print (e.g., “1-5,8,11-13”)
prefer_css_page_size - Use CSS-defined page size (default: False)
Saving page snapshots
Capture complete page state in MHTML format:
# Save complete page with resources
await tab.save_snapshot( "page.mhtml" )
# Load in Chrome/Edge to view offline
MHTML files contain the HTML, CSS, images, and other resources in a single file, perfect for offline viewing.
Complete screenshot example
Example from the source repository:
import asyncio
import zendriver as zd
from pathlib import Path
async def screenshot_github_project ():
browser = await zd.start()
# Navigate to project page
tab = await browser.get(
"https://github.com/ultrafunkamsterdam/undetected-chromedriver"
)
# Wait for page to fully load
await tab.wait_for_ready_state( "complete" )
# Save screenshot
save_path = Path( "screenshot.jpg" ).resolve()
await tab.save_screenshot(save_path)
print ( f "Screenshot saved to { save_path } " )
# Close the tab
await tab.close()
await browser.stop()
if __name__ == "__main__" :
asyncio.run(screenshot_github_project())
Adapted from examples/imgur_upload_image.py
Screenshot debugging tips
Visual element highlighting
Highlight elements before taking screenshots:
element = await tab.select( ".target-element" )
# Flash to highlight
await element.flash( duration = 0.5 )
# Take screenshot while highlighted
await tab.save_screenshot( "highlighted.jpg" )
Ensure elements are visible:
element = await tab.find( "Important Section" )
await element.scroll_into_view()
await tab.sleep( 0.5 ) # Wait for scroll animation
await element.save_screenshot( "section.jpg" )
Wait before screenshot
Wait for animations and lazy-loaded content:
await tab.get( "https://example.com" )
# Wait for images to load
await tab.wait_for_ready_state( "complete" )
# Additional wait for any animations
await tab.sleep( 1 )
await tab.save_screenshot( "complete.jpg" , full_page = True )
Bulk screenshot operations
Capture multiple pages efficiently:
import asyncio
import zendriver as zd
async def screenshot_urls ( urls ):
browser = await zd.start()
for i, url in enumerate (urls):
try :
tab = await browser.get(url)
await tab.wait_for_ready_state( "complete" )
await tab.save_screenshot( f "screenshot_ { i } .jpg" , full_page = True )
print ( f "Captured: { url } " )
except Exception as e:
print ( f "Failed to capture { url } : { e } " )
await browser.stop()
async def main ():
urls = [
"https://example1.com" ,
"https://example2.com" ,
"https://example3.com"
]
await screenshot_urls(urls)
if __name__ == "__main__" :
asyncio.run(main())
Screenshot comparison
Capture before and after screenshots:
# Before interaction
await tab.save_screenshot( "before.png" , format = "png" )
# Interact with page
button = await tab.find( "Toggle Dark Mode" )
await button.click()
await tab.sleep( 0.5 )
# After interaction
await tab.save_screenshot( "after.png" , format = "png" )
Handling screenshot errors
Gracefully handle screenshot failures:
import asyncio
try :
await tab.save_screenshot( "page.jpg" , full_page = True )
except Exception as e:
print ( f "Screenshot failed: { e } " )
# Fallback to viewport screenshot
try :
await tab.save_screenshot( "page-viewport.jpg" , full_page = False )
except Exception as e2:
print ( f "Fallback also failed: { e2 } " )
Screenshot operations may fail if the page hasn’t finished loading. Always wait for the ready state or specific elements before capturing.
Next steps
Network monitoring Monitor and intercept network requests
Cookies and sessions Manage cookies and session data