Python Selenium: File upload in headless mode with button element

I’m stuck trying to upload a file using Selenium with Python in headless mode. The upload button is a type="button" element, not a file input. Here’s what the button looks like:

<button id="upload-button" type="button">
  Choose files...
</button>

I tried using send_keys(), but it didn’t work:

upload_btn = driver.find_element_by_id('upload-button')
upload_btn.send_keys('/path/to/file.pdf')

I also tried pyautogui to handle the file dialog, but it doesn’t work in headless mode.

Any ideas on how to upload files in this situation? I need a solution that works in headless mode with Selenium and Python. Thanks!

Having dealt with similar issues, I can suggest an alternative approach using the Chrome DevTools Protocol (CDP). This method works well in headless mode and doesn’t rely on visible elements.

First, enable CDP with your WebDriver:

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

caps = DesiredCapabilities.CHROME.copy()
caps['goog:chromeOptions'] = {'debuggerAddress': 'localhost:9222'}
driver = webdriver.Chrome(desired_capabilities=caps)

Then, use the Page.setFileInputFiles command to upload the file:

file_path = '/path/to/file.pdf'
driver.execute_cdp_cmd('Page.setFileInputFiles', {'files': [file_path]})

This bypasses the need for a visible file input and works reliably in headless mode. Remember to adjust the Chrome debugging port if needed.

try triggering the file input with js directly. for instance, run driver.execute_script(‘document.getElementById(“upload-button”).click()’) then use send_keys() on the file input that appears. works in headless mode too. hope it helps!

I’ve faced similar challenges with file uploads in headless Selenium. One workaround that’s worked for me is using JavaScript to bypass the button click and directly trigger the file input. Here’s what you can try:

Locate the hidden file input element—which is usually present but not visible—then use JavaScript to make it visible and clickable. Finally, use send_keys() on this element instead of the button.

For example:

# Find the hidden file input
file_input = driver.find_element_by_css_selector('input[type="file"]')

# Make it visible
driver.execute_script("arguments[0].style.display = 'block';", file_input)

# Now use send_keys
file_input.send_keys('/path/to/file.pdf')

This approach has worked for me in headless mode across different browsers. Adjust the selector for finding the file input based on your specific page structure.