Skip to main content

BAREBIDI

Async Firefox automation for Python, speaking WebDriver BiDi straight to the browser

  • built for Firefox: the regular Firefox you download from Mozilla, nothing patched
  • no driver binaries: no geckodriver, no Selenium
  • no Playwright-style browser builds: it drives the Firefox you already have
  • one dependency: websockets

WebDriver BiDi is the W3C standard for browser automation, the successor of both classic WebDriver and the Chrome DevTools Protocol. Firefox speaks it natively: barebidi opens a websocket to Firefox and talks to it directly, so there is nothing in between to install, keep in sync or get detected.

Most automation tools are Chrome-first and treat Firefox as an afterthought. barebidi is the other way around: every feature is built and tested against Firefox, including its quirks with cross-site iframes, closed shadow roots and input routing. Chrome support may come later; Firefox is the point.

Some features

  • tab.select(css), tab.find(text), tab.xpath(expr), all waiting until the element appears
  • chainable queries: await tab.select("form").select("button").click()
  • mouse and keyboard input with human timings and trajectories, on by default
  • elements inside cross-site iframes and closed shadow roots, the ones scripts can't reach
  • proxies with credentials (no password prompt), plus per-context locale and time zone
  • isolated browser contexts: separate cookies, storage and proxy in one browser
  • network: intercept, block or modify requests, wait for responses, extra headers
  • dialogs (alert, confirm, prompt), file uploads and downloads
  • cookies: read, set, clear
  • every protocol type generated from the spec's CDDL, so your editor knows the shape of every message

Installation

barebidi needs Firefox installed, from Mozilla (on Ubuntu, not the snap). It finds it in the usual places; otherwise set FIREFOX_PATH or pass executable_path=. On a headless server, run Firefox under Xvfb (or use headless=True).

pip install barebidi

To update:

pip install -U barebidi

Usage examples

The browser starts with sensible defaults: a temporary profile, stealth and human input on. Everything is async, and the browser closes itself when the async with block ends.

Simple

import asyncio
import barebidi as bb

async def main():
    async with bb.Browser() as browser:
        tab = await browser.get("https://example.com")
        print(await tab.title())

        ... further code ...

if __name__ == "__main__":
    asyncio.run(main())

Or without the block, nodriver style:

browser = await bb.start()
tab = await browser.get("https://example.com")
...
await browser.stop()

Custom starting options

browser = await bb.start(
    headless=True,
    screen=(1920, 1080),                    # headless only; a real window keeps the real screen
    profile="/path/to/profile",             # keep cookies and storage between runs
    proxy="http://user:pass@host:8080",     # credentials are handled for you
    locale="en-US",
    timezone="America/New_York",
    executable_path="/path/to/firefox",     # or set FIREFOX_PATH
    args=["--some-firefox-arg"],
    prefs={"some.pref": True},              # Firefox about:config prefs
    downloads="/path/to/downloads",
    humanize=True,                          # human-like mouse and keyboard
)

Finding and using elements

tab = await browser.get("https://example.com/login")

await tab.select("input[name=user]").type("me@example.com")
await tab.select("input[name=password]").type("secret")
await tab.find("Sign in").click()

items = await tab.select_all(".item")
for item in items:
    print(await item.text(), await item.get_attribute("href"))

Inside an iframe or a shadow root:

frame = await tab.frame("widget.example.org")         # by URL
button = await frame.select("button")

root = await (await tab.select("my-component")).shadow_root()
await root.select("input").type("hello")

Network

async with tab.expect_response("/api/items") as expected:
    await tab.find("Load more").click()
items = await expected.response.json()

await tab.block(resource_types=["image", "font"])
await tab.set_extra_headers({"X-Debug": "1"})

Dialogs, uploads and downloads

async def answer(dialog):
    await dialog.accept("Barebidi")

await tab.on_dialog(answer)

await tab.select("input[type=file]").set_files("report.pdf")

async with tab.expect_download() as expected:
    await tab.find("Export CSV").click()
path = await expected.download.path()

Separate identities in one browser

us = await browser.new_context(proxy="http://user:pass@us-proxy:8080", locale="en-US", timezone="America/Chicago")
de = await browser.new_context(proxy="http://user:pass@de-proxy:8080", locale="de-DE", timezone="Europe/Berlin")

await us.get("https://example.com")
await de.get("https://example.com")   # different cookies, storage, IP, language and clock

Raw protocol

Anything barebidi doesn't wrap is one call away:

result = await tab.send("browsingContext.captureScreenshot", {"context": tab.id, "origin": "document"})

Note: driving Camoufox

Camoufox is a patched Firefox build. Since it is still Firefox and still speaks WebDriver BiDi, barebidi drives it like any other Firefox: point executable_path at its binary.

  1. Download Camoufox with its own tool:

    pip install camoufox
    python -m camoufox fetch
    
  2. Find the binary it downloaded:

    • macOS: ~/Library/Caches/camoufox/browsers/official/<version>/Camoufox.app/Contents/MacOS/camoufox
    • Linux: ~/.cache/camoufox/browsers/official/<version>/camoufox
  3. Start barebidi with that path. Turn barebidi's own stealth off: Camoufox handles that part itself.

    browser = await bb.start(executable_path="/path/to/camoufox", stealth=False)
    
  4. Optionally, pass Camoufox its settings as JSON in the CAMOU_CONFIG_1 environment variable through env=. A config longer than 32767 characters goes in pieces: CAMOU_CONFIG_1, CAMOU_CONFIG_2, ... See Camoufox's documentation for the available keys.

    import json
    
    config = {"navigator.hardwareConcurrency": 8}
    browser = await bb.start(
        executable_path="/path/to/camoufox",
        stealth=False,
        env={"CAMOU_CONFIG_1": json.dumps(config)},
    )
    

The camoufox package is only needed to download the browser; barebidi doesn't depend on it.

Metadata

Release files for barebidi 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for barebidi 0.1.0
File Size Uploaded Provenance Actions
barebidi-0.1.0.tar.gz 101.9 kB Without provenance Details for barebidi-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for barebidi 0.1.0
File Interpreter ABI Platform Actions
barebidi-0.1.0-py3-none-any.whl Python 3 none any Details for barebidi-0.1.0-py3-none-any.whl

Total release size: 153.5 kB

Release files / barebidi-0.1.0.tar.gz

Download URL barebidi-0.1.0.tar.gz
Size 101.9 kB
Tags Source
SHA-256 checksum
How to use checksums
085305c07100e75f2606e66160f72b472b7ba2bf24b89e5cd0c85ca4245d1d95
BLAKE2b-256 checksum
How to use checksums
a9fe6bb4a788f4af0106ed0424daa1e97eceab1e43f18169fa484e86dc363ffe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / barebidi-0.1.0-py3-none-any.whl

Download URL barebidi-0.1.0-py3-none-any.whl
Size 51.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a1b10cb5cb5866fdac9ab7d7886803b644800018922a71e886aacc2838f9f677
BLAKE2b-256 checksum
How to use checksums
4025ce1ee666602e65355743eef66cb3c39f2407bbed96611940d4aa483309ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for barebidi 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page