### README # Welcome to Helium's documentation The documentation is built using [sphinx](https://www.sphinx-doc.org/en/master/index.html) and the theme used is [sphinx-rtd-theme](https://sphinx-rtd-theme.readthedocs.io/en/stable/). ## Setting up documentation locally Ensure you have `python` and `pip` installed on your system and then run this command in the project root: ```bash pip install -Ur requirements/docs.txt make -C docs/ html ``` This will install all development dependencies for the project and then build the documentation in HTML format in `docs/_build/` directory. Open `docs/_build/index.html` in your browser to see the documentation. --- ### Cheatsheet # Helium cheatsheet This page very quickly teaches you the most important parts of Helium's API. ## Importing All of Helium's public functions lie directly in the module `helium`. You can for instance import them as follows: ```python from helium import * ``` ## Starting a browser Helium currently supports Chrome and Firefox. You can start them with the following functions: ```python start_chrome() start_firefox() ``` You can optionally pass a URL to open (eg. `start_chrome('google.com')`) ## Headless browser When you type the above commands, you will actually see a browser window open. This is useful for developing your scripts. However, once you run them, you may not want this window to appear. You can achieve this by adding `headless=True`: ```python start_chrome(headless=True) start_chrome('google.com', headless=True) ``` (Similarly for `start_firefox(...)` of course.) ## Interacting with a web site The following example shows the most typical statements in a Helium script: ```python from helium import * start_chrome('google.com') write('helium selenium github') press(ENTER) click('mherrmann/helium') go_to('github.com/login') write('username', into='Username') write('password', into='Password') click('Sign in') kill_browser() ``` Most of your own code will (hopefully) be as simple as the above. ## Element types The above example used pure strings such as `Sign in` to identify elements on the web page. But Helium also lets you target elements more specifically. For instance: * [`Link('Sign in')`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L643) * [`Button('Sign in')`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L706) * [`TextField('First name')`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L768) * [`CheckBox('I accept')`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L867) * [`RadioButton('Windows')`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L907) * [`Image(alt='Helium logo')`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L739) You can pass them into other functions such as `click(Link('Sign in'))`. But you can also use them to _read_ data from the web site. For instance: ```python print(TextField('First name').value) ``` A common use case is to use `.exists()` to check for the existence of an element. For example: ```python if Text('Accept cookies?').exists(): click('I accept') ``` I also often find `Text(...).value` useful for reading out data: ```python name = Text(to_right_of='Name:', below=Image(alt='Profile picture')).value ``` For a full list of element types and their properties, please see [the source code](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L470-L1008). ## Finding elements relative to others You already saw in the previous section how `above=...` and `to_right_of=...` let you find elements relative to other elements. You can similarly use `below=...` and `to_left_of`. Here are some more examples. ```python Text(above='Balance', below='Transactions').value Link(to_right_of='Invoice:') Image(to_right_of=Link('Sign in', below=Text('Navigation'))) ``` ## Waiting for elements to appear (or other conditions) Use [`wait_until(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L410) to wait for a condition to become true. For example: ```python wait_until(Button('Download').exists) ``` But you can also use this to wait for an arbitrary condition: ```python wait_until(lambda: TextField('Balance').value == '$2M') ``` ## jQuery-style selectors Sometimes, you do need to fall back to using HTML IDs, CSS Selectors or XPaths to identify an element on the web page. Helium's [`S(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L568) predicate lets you do this. The parameter you pass to it is interpreted as follows: * If it starts with an ``@``, then it identifies elements by HTML ``name``. Eg. ``S("@btnName")`` identifies an element with ``name="btnName"``. * If it starts with ``//``, then Helium interprets it as an XPath. * Otherwise, Helium interprets it as a CSS selector. This in particular lets you write ``S("#myId")`` to identify an element with ``id="myId"``, or ``S(".myClass")`` to identify elements with ``class="myClass"``. As before, you can combine `S(...)` with other functions such as `click(S(...))`, or use it to extract data. For an example of this, see [below](#finding-all-elements). ## Combining Helium and Selenium's APIs All Helium does is translate your high-level commands into low-level Selenium function calls. Because of this, you can freely mix Selenium and Helium. For example: ```python # A Helium function: driver = start_chrome() # A Selenium API: driver.execute_script("alert('Hi!');") ``` You can also get / set the Selenium WebDriver which Helium uses via [`get_driver()`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L104) and [`set_driver(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L97). With the WebDriver instance, you can execute any Selenium commands you want. To use Helium's API's to obtain Selenium `WebElement`s, use the `.web_element` property of Helium's various GUI elements. For instance: ```python # Get the CSS class of the "Helium" link: Link('Helium').web_element.get_attribute('class') ``` Here, `.get_attribute(...)` is a Selenium API. ## Finding all elements The `.web_element` property and the `S(...)` predicate are particularly useful for extracting multiple pieces of data from a web page. To do this, you can use Helium's [`find_all(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L281) function. As its name implies, it lets you find all occurrences of an element on a page. For example: ```python email_cells = find_all(S("table > tr > td", below="Email")) emails = [cell.web_element.text for cell in email_cells] ``` ## Implicit waits When you issue a command such as `click('Download')`, Helium by default waits up to 10 seconds for the respective element to appear. This feature is called "implicit waiting". You can change the 10 second default to a different value via the [`Config` class](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L437): ```python Config.implicit_wait_secs = 30 ``` However, before you do this, it may be better to add explicit waits to your code, such as `wait_until(Button('Download').exists)`. ## Alerts The [`Alert` class](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L970) lets you interface with JavaScript popup boxes. Use `Alert().accept()`, `Alert().dismiss()` to click "Ok" or "Cancel", `Alert().text` to read the message shown, or `write(..., into=Alert())` to enter a value. ## File uploads, drag and drop, combo boxes, popups Use [`attach_file(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L388), [`drag_file(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L375), [`drag(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L252), [`select(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L362), [`switch_to(...)`](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L1057). ## Clicking at x, y coordinates Sometimes, you may want to click at a specific `(x, y)` coordinate, or at an offset of an element. ### Create a `Point` to specify the coordinates and use it to click: ```python from helium import click point = Point(x=100, y=200) click(point) # Clicks at (100, 200) ``` ### Adjusting a Point by a offset You can modify a point's position using addition or subtraction by a delta: ```python delta = (20, -10) click(Point(100, 200) + delta) # Clicks at (120, 190) ``` See the [`Point` class](https://github.com/mherrmann/helium/blob/0667ddb9be531367a0d707ad8f5fcfb75c528521/helium/__init__.py#L1010) for more. ## Taking a screenshot Use Selenium's API: ```python get_driver().save_screenshot(r'C:\screenshot.png') ``` Note the leading `r`. This is required because the string contains a backslash `\`. --- ### Contributors Contributors to this project ============================ .. Please use this format to add your contributions to this file `SocialUsernameName `_ (**Your Name**) - *Description of your contribution in a few words* - `mherrmann `_ (**Michael Herrmann**) - *Project creator and maintainer* - `IgnisDa `_ (**Diptesh Choudhuri**) - *Documentation maintainer* --- ### Index Welcome to Helium's documentation! ================================== Helium is a Python library for automating web sites. It is based on `Selenium-python `_. Selenium is great, but difficult to use. Helium wraps around Selenium to give you a simpler API. Helium's name comes from being a lighter chemical element than Selenium. For a quick overview of Helium's features, please see `the project home page `_. Here, in the documentation, you will find a more comprehensive reference. .. toctree:: :maxdepth: 2 :caption: Contents: installation.rst api.rst contributors.rst Indices and tables ================== * :ref:`genindex` * :ref:`search` --- ### Installation Installation ============ To install Helium, you need Python 3 and Chrome or Firefox. If you already know Python, then the following command should be all you need: .. code-block:: bash pip install helium Otherwise - Hi! I would recommend you create a virtual environment in the current directory. Any libraries you download (such as Helium) will be placed there. Enter the following into a command prompt: .. code-block:: bash python3 -m venv venv This creates a virtual environment in the `venv/` directory. To activate it: .. code-block:: bash # On Mac/Linux, bash shell: source venv/bin/activate # On Windows: call venv\Scripts\activate.bat Then, install Helium using `pip`: .. code-block:: bash python -m pip install helium Now enter :code:`python` into the command prompt and the command :code:`from helium import *` and you are ready to get started! --- ### README # Lighter web automation with Python Helium is a Python library for automating browsers such as Chrome and Firefox. For example: ## Installation To get started with Helium, you need Python 3 and Chrome or Firefox. I would recommend creating a virtual environment. This lets you install Helium for just your current project, instead of globally on your whole computer. To create and activate a virtual environment, type the following commands into a command prompt window: ```bash python3 -m venv venv # On Mac/Linux: source venv/bin/activate # On Windows: call venv\scripts\activate.bat ``` Then, you can install Helium with `pip`: ```bash python -m pip install helium ``` Now enter `python` into the command prompt and (for instance) the commands in the animation at the top of this page (`from helium import *`, ...). ## Your first script I've compiled a [cheatsheet](docs/cheatsheet.md) that quickly teaches you all you need to know to be productive with Helium. For a more complete reference of Helium's features, please see the [documentation](https://helium.readthedocs.io/en/latest/). ## Connection to Selenium Under the hood, Helium forwards each call to Selenium. The difference is that Helium's API is much more high-level. In Selenium, you need to use HTML IDs, XPaths and CSS selectors to identify web page elements. Helium on the other hand lets you refer to elements by user-visible labels. As a result, Helium scripts are typically 30-50% shorter than similar Selenium scripts. What's more, they are easier to read and more stable with respect to changes in the underlying web page. Because Helium is simply a wrapper around Selenium, you can freely mix the two libraries. For example: ```python # A Helium function: driver = start_chrome() # A Selenium API: driver.execute_script("alert('Hi!');") ``` So in other words, you don't lose anything by using Helium over pure Selenium. In addition to its more high-level API, Helium simplifies further tasks that are traditionally painful in Selenium: - **iFrames:** Unlike Selenium, Helium lets you interact with elements inside nested iFrames, without having to first "switch to" the iFrame. - **Window management.** Helium notices when popups open or close and focuses / defocuses them like a user would. You can also easily switch to a window by (parts of) its title. No more having to iterate over Selenium window handles. - **Implicit waits.** By default, if you try click on an element with Selenium and that element is not yet present on the page, your script fails. Helium by default waits up to 10 seconds for the element to appear. - **Explicit waits.** Helium gives you a much nicer API for waiting for a condition on the web page to become true. For example: To wait for an element to appear in Selenium, you would write: ```python element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement")) ) ``` With Helium, you can write: ```python wait_until(Button('Download').exists) ``` ## Status of this project I have too little spare time to maintain this project for free. If you'd like my help, please go to my [web site](http://herrmann.io) to ask about my consulting rates. Otherwise, unless it is very easy for me, I will usually not respond to emails or issues on the issue tracker. I will however accept and merge PRs. So if you add some functionality to Helium that may be useful for others, do share it with us by creating a Pull Request. For instructions, please see [Contributing](#Contributing) below. ## How you can help I find Helium extremely useful in my own projects and feel it should be more widely known. Here's how you can help with this: - Star this project on GitHub. - Tell your friends and colleagues about it. - [Share it on Twitter with one click](https://twitter.com/intent/tweet?text=I%20find%20Helium%20very%20useful%20for%20web%20automation%20with%20Python%3A%20https%3A//github.com/mherrmann/helium) - Share it on other social media - Write a blog post about Helium. With this, I think we can eventually make Helium the de-facto standard for web automation in Python. ## Contributing Pull Requests are very welcome. Please follow the same coding conventions as the rest of the code, in particular the use of tabs over spaces. Also, read through my [PR guidelines](https://gist.github.com/mherrmann/5ce21814789152c17abd91c0b3eaadca). Doing this will save you (and me) unnecessary effort. Before you submit a PR, ensure that the tests still work: ```bash pip install -Ur requirements/test.txt python setup.py test ``` This runs the tests against Chrome. To run them against Firefox, set the environment variable `TEST_BROWSER` to `firefox`. Eg. on Mac/Linux: ```bash TEST_BROWSER=firefox python setup.py test ``` On Windows: ```bash set TEST_BROWSER=firefox python setup.py test ``` If you do add new functionality, you should also add tests for it. Please see the [`tests/`](tests) directory for what this might look like. ## History I (Michael Herrmann) originally developed Helium in 2013 for a Polish IT startup called BugFree software. (It could be that you have seen Helium before at https://heliumhq.com.) We shut down the company at the end of 2019 and I felt it would be a shame if Helium simply disappeared from the face of the earth. So I invested some time to modernize it and bring it into a state suitable for open source. Helium used to be available for both Java and Python. But because I now only use it from Python, I didn't have time to bring the Java implementation up to speed as well. Similarly for Internet Explorer: Helium used to support it, but since I have no need for it, I removed the (probably broken) old implementation. The name Helium was chosen because it is also a chemical element like Selenium, but it is lighter. ---