Back to Articles

Helium: Why Your Selenium Scripts Are 50% Longer Than They Need to Be

[ View on GitHub ]

Helium: Why Your Selenium Scripts Are 50% Longer Than They Need to Be

Hook

What if you could click a button by simply writing click('Download') instead of wrestling with driver.find_element(By.XPATH, "//button[contains(text(), 'Download')]").click()? Helium makes browser automation feel like describing what you see, not hunting through the DOM.

Context

Web automation with Selenium has remained fundamentally unchanged since its inception: you identify elements using technical artifacts like CSS selectors, XPath expressions, and HTML IDs. This creates a mismatch between how humans perceive web pages (“click the Download button”) and how we must instruct our code (“find the element with class ‘btn-primary’ nested under div with ID ‘container-3’”). Worse, these technical selectors are brittle—a minor redesign can break dozens of tests.

Helium emerged from this frustration at BugFree software, a Polish IT startup where developer Michael Herrmann built it in 2013 to speed up automation work. The insight was simple but powerful: Selenium handles the hard work of browser control beautifully, but its API requires you to think like a DOM parser rather than a user. Helium acts as a translator, letting you reference elements by their visible labels while forwarding every call to Selenium underneath. After BugFree shut down in 2019, Herrmann open-sourced a modernized Python-only version, and it’s now accumulated over 8,000 GitHub stars from developers who’ve experienced the productivity boost firsthand.

Technical Insight

Helium Abstraction

click('Submit')

write('text', into='Field')

Human-readable labels

XPath/CSS selectors

Element queries with retries

WebDriver protocol

Element state/responses

Result status

User Script

Helium API Calls

Helium Wrapper Layer

Label-based Commands

Element Translator

Text to Selectors

Wait Handler

Implicit Retries

Selenium WebDriver

Browser Control

Web Browser

Chrome/Firefox/Edge

System architecture — auto-generated

Helium’s architecture is deceptively straightforward: it’s a wrapper around Selenium WebDriver that transforms human-readable commands into the CSS selectors and XPath queries Selenium requires. When you call click('Submit'), Helium searches for clickable elements containing that text—buttons, links, or inputs—without you needing to specify element type or location. This label-based approach typically results in scripts 30-50% shorter than equivalent Selenium code.

Consider a typical login flow. In pure Selenium, you’d write:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get('https://example.com/login')
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "username"))
)
driver.find_element(By.ID, "username").send_keys("user@example.com")
driver.find_element(By.ID, "password").send_keys("secret123")
driver.find_element(By.XPATH, "//button[text()='Log In']").click()

With Helium, the same workflow becomes:

from helium import *

start_chrome('https://example.com/login')
write('user@example.com', into='Username')
write('secret123', into='Password')
click('Log In')

Notice the elimination of explicit waits—Helium includes a 10-second implicit wait by default, automatically retrying element lookups until they appear or the timeout expires. The into parameter demonstrates Helium’s context awareness: it finds the input field associated with the ‘Username’ label, handling common HTML patterns like <label for="..."> or labels wrapping inputs.

One of Helium’s most significant advantages is iframe handling. Selenium requires you to explicitly switch context before interacting with iframe content, then switch back afterward. Helium detects when target elements live inside iframes and handles the context switching automatically. If you need to click a button three iframes deep, you write the same click('Button') command you’d use for a top-level element.

Window management is similarly simplified. When a popup opens, Helium automatically focuses it, mimicking user behavior. You can switch between windows by title fragments:

click('Open Help')
switch_to('Help Documentation')  # Matches partial title
# Interact with help window
kill_browser()  # Closes help window and returns focus

Crucially, Helium doesn’t lock you in. Since every Helium function ultimately calls Selenium, you can freely mix APIs:

driver = start_chrome()
click('Advanced Settings')  # Helium
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  # Selenium
wait_until(Button('Save').exists)  # Helium

This interoperability means you can use Helium for 90% of your script where the high-level API shines, then drop down to Selenium for edge cases requiring JavaScript execution, cookie manipulation, or other low-level control. The wait_until function shown above demonstrates Helium’s cleaner explicit wait syntax compared to Selenium’s verbose WebDriverWait pattern, accepting simple boolean conditions rather than expected_conditions objects.

Gotcha

The project’s README is refreshingly honest about its maintenance status: the author explicitly states he has “too little spare time to maintain this project for free” and will typically not respond to issues unless paid for consulting. This undermaintained status is the elephant in the room—you’re adopting a library where bug reports may go unanswered and feature requests will likely require you to submit your own pull requests. For production environments requiring vendor support or guaranteed compatibility with future browser versions, this is a non-starter.

Browser support is limited to Chrome and Firefox. Internet Explorer support was explicitly removed, and there’s no WebKit/Safari option. The high-level abstractions, while convenient, can obscure performance implications—Helium’s text-matching searches are necessarily slower than direct ID or CSS selector lookups. In scenarios requiring thousands of element interactions or sub-second performance, you may need to bypass Helium for critical paths. Additionally, the label-based approach assumes stable visible text, which can be problematic for internationalized applications where button labels change based on locale. Helium also only supports Python; the original Java implementation was discontinued.

Verdict

Use Helium if you’re writing Python automation scripts where developer productivity and maintainability matter more than raw performance, and you’re comfortable with limited community support. It excels at web scraping, automating repetitive browser tasks, or writing integration tests focused on user-facing workflows where identifying elements by visible labels produces more stable tests than brittle CSS selectors. The seamless Selenium interoperability means there’s minimal lock-in risk—you can always drop down to raw Selenium when needed. Skip Helium if you need enterprise-grade support, are automating browsers beyond Chrome/Firefox, require maximum performance for high-volume operations, work with heavily internationalized UIs where visible text changes frequently, or need assurance that the library will receive active maintenance and security updates. The undermaintained status makes it best suited for internal tools, personal projects, or teams with the technical capacity to fork and maintain their own version if necessary.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/automation/mherrmann-helium.svg)](https://starlog.is/api/badge-click/automation/mherrmann-helium)