Python Playwright File Upload Download and Screenshots
python playwright file upload download and screenshots: Learn how to automate file uploads, handle downloads, and capture screenshots with Python Playwright, including...
python playwright file upload download and screenshots requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When automating a web application with Python Playwright, file upload, download, and screenshot operations each require a different approach than simple clicks or text entry. Uploads may involve hidden input elements or OS-level file chooser dialogs. Downloads need to be captured as events and saved to disk. Screenshots must account for full-page versus element-level capture. This article covers the practical implementation of all three operations using Playwright's Python API, with code examples you can adapt directly to your test or scraping scripts.
Why File Operations Need Special Handling in Playwright
Playwright runs in the browser context and cannot interact with native OS dialogs directly. When a user clicks a button that opens a file picker, the browser triggers a file chooser event. Playwright intercepts this event and lets you provide file paths programmatically. Similarly, downloads are initiated by the browser but Playwright exposes them as events so you can control where the file is saved. Screenshots, on the other hand, are a direct browser API and are simpler to implement, but you still need to choose the right capture mode for your use case.
Setting Up Playwright for File Upload and Download
Before any file operation, you need a browser context with downloads enabled. By default, Playwright's browser context does not accept downloads unless you explicitly set accept_downloads=True. Here is a minimal setup:
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=False) context = browser.new_context(accept_downloads=True) page = context.new_page() # your automation code here browser.close()
If you are using the async API, the pattern is similar with async with and await on each call. The accept_downloads flag is essential; without it, expect_download will never fire.
Uploading Files with set_input_files
The most straightforward upload scenario is when the page contains an <input type="file"> element. Playwright's set_input_files method sets the file paths directly on the input, bypassing the file chooser entirely. This works for single or multiple files.
page.goto("https://example.com/upload") page.set_input_files("#file-input", "report.pdf") # For multiple files: page.set_input_files("#file-input", ["report.pdf", "data.csv"])
You can also pass file content as bytes instead of paths, which is useful when the file is generated in memory:
file_content = b"name,age\nAlice,30\nBob,25" page.set_input_files("#file-input", {"name": "data.csv", "mimeType": "text/csv", "buffer": file_content})
The method waits for the input to be attached and visible, and it automatically scrolls the element into view if needed. After setting the files, you can submit the form or trigger the upload via JavaScript as the application requires.
Handling File Chooser Dialogs
When the upload is triggered by clicking a button that opens a native file picker, you need to use expect_file_chooser. This context manager waits for the file chooser event and gives you a FileChooser object to set files on.
with page.expect_file_chooser() as fc_info: page.click("#upload-button") file_chooser = fc_info.value file_chooser.set_files("image.png")
The set_files method on the file chooser accepts the same inputs as set_input_files: a single path, a list of paths, or a dictionary with buffer. This pattern is essential for uploads that do not use a plain input element, such as drag-and-drop zones that trigger a file picker on click.
Downloading Files with expect_download
Downloads are handled with expect_download, which waits for a download event after an action. The resulting Download object provides methods to access the suggested filename and save the file to a local path.
with page.expect_download() as download_info: page.click("#download-button") download = download_info.value print(download.suggested_filename) download.save_as("/path/to/save/" + download.suggested_filename)
The save_as method writes the file to the specified location. If you only need the file's temporary path, you can call download.path(), which returns the path where Playwright has stored the file in its temporary directory. This is useful if you want to read the content without saving it permanently.
with page.expect_download() as download_info: page.click("#download-button") download = download_info.value file_path = download.path() with open(file_path, "r") as f: content = f.read()
Note that download.path() blocks until the download completes, so you do not need to add an extra wait. However, if the download fails or takes too long, it will raise a timeout error.
Taking Screenshots with page.screenshot
Playwright's page.screenshot captures the visible viewport by default. To capture the full scrollable page, pass full_page=True. You can also capture a specific element using locator.screenshot().
# Full page screenshot page.screenshot(path="full_page.png", full_page=True) # Element screenshot page.locator(".header").screenshot(path="header.png") # Screenshot with custom clip page.screenshot(path="clip.png", clip={"x": 0, "y": 0, "width": 500, "height": 500})
The default format is PNG, but you can use JPEG by specifying type="jpeg" and adjusting the quality parameter (0–100). For large pages, full-page screenshots can be memory-intensive; consider capturing only the needed region or using JPEG with lower quality to reduce file size.
Waiting for Downloads and Managing Timeouts
expect_download accepts a timeout parameter in milliseconds. If the download event does not occur within that window, a TimeoutError is raised. This is useful when you expect a download to be triggered only after some asynchronous action.
with page.expect_download(timeout=10000) as download_info: page.click("#slow-download")
Similarly, download.path() and download.save_as() block until the file is fully written. If you need to set an overall timeout for the download operation, you can wrap the call in a page.wait_for_timeout or use the timeout argument of the context manager. Be aware that the download event fires when the browser starts the download, not when it finishes. The path() and save_as() methods wait for completion, so a slow server may cause the script to hang. In such cases, consider increasing the default timeout or using a separate thread to monitor progress.
Common Pitfalls and Edge Cases
One common issue is that set_input_files does not work on elements that are not <input type="file">. For custom upload widgets, always use the file chooser approach. Another pitfall is forgetting to set accept_downloads=True; without it, expect_download will time out. In headless mode, downloads work the same way, but you may need to specify a download path if the browser's default download directory is not writable.
When handling multiple downloads, each expect_download context must be paired with the action that triggers that specific download. If you click a button that triggers two downloads simultaneously, you need to use page.expect_download with lambda predicates to distinguish them, or use page.on("download") to listen for all download events. The latter is more flexible but requires manual event handling.
Screenshots can also fail if the page is not fully loaded or if the element is not in the viewport. Playwright automatically scrolls elements into view for element screenshots, but for full-page screenshots, ensure the page has settled (e.g., after network idle) to avoid capturing incomplete content.
Finally, remember that file paths in set_input_files and save_as are resolved relative to the current working directory. For robust scripts, use absolute paths or construct paths with os.path.join to avoid platform-specific separator issues.