Skip to main content

jsEasy

jsEasy (jseasy) is a lightweight Python runtime for scraping and testing HTML pages that need JavaScript execution, but not a full browser.

It gives Python code a browser-like DOM, executes page scripts with QuickJS, supports common network APIs such as fetch() and XMLHttpRequest, and returns the final DOM for extraction.

requests + BeautifulSoup
< jsEasy
< Playwright / Selenium / real browsers

jsEasy is designed for the middle ground: pages where JavaScript mutates the DOM, loads JSON, runs timers, or executes small modules, but where layout, pixels, GPU APIs, and browser fingerprint parity are unnecessary.

Highlights

  • No browser process: no Chromium download, no WebDriver, no browser startup cost.
  • Python-first API: Page.open(), Page.from_html(), select(), select_all(), eval(), html().
  • Browser-like runtime: DOM, events, timers, storage, history, location, CSSOM, fetch, XHR, module scripts.
  • Scraper-friendly behavior: failed third-party scripts are collected in diagnostics instead of aborting the whole page.
  • Typed package: ships py.typed.
  • PyPI-ready: wheel/sdist build, docs, examples, tests, and release checklist.

Installation

pip install jseasy

Python 3.10+ is supported.

Quick Start

from jseasy import Page

page = Page.from_html("""
<main id="app"></main>
<script>
  const title = document.createElement("h1");
  title.textContent = "Loaded without Chrome";
  document.querySelector("#app").appendChild(title);
</script>
""")

print(page.select("#app h1").text)

Output:

Loaded without Chrome

Loading A Real Page

from jseasy import Page

with Page.open("https://example.com") as page:
    print(page.select("h1").text)
    print(len(page.html()))

Page.open() fetches the URL, parses HTML, loads stylesheets, executes scripts, drains pending work, and gives you the current DOM.

Fetch And XHR

import httpx
from jseasy import Page

def handler(request: httpx.Request) -> httpx.Response:
    return httpx.Response(200, json={"name": "Ada"})

client = httpx.Client(transport=httpx.MockTransport(handler))

page = Page.from_html(
    """
    <div id="name"></div>
    <script>
      fetch("/api")
        .then((response) => response.json())
        .then((data) => {
          document.querySelector("#name").textContent = data.name;
        });
    </script>
    """,
    url="https://example.test",
    client=client,
)

print(page.select("#name").text)

Module Scripts

import httpx
from jseasy import Page

def handler(request: httpx.Request) -> httpx.Response:
    if request.url.path == "/app.js":
        return httpx.Response(
            200,
            text="""
            import { label } from "./labels.js";
            document.querySelector("#app").textContent = label;
            """,
        )
    return httpx.Response(200, text='export const label = "module loaded";')

client = httpx.Client(transport=httpx.MockTransport(handler))

page = Page.from_html(
    '<div id="app"></div><script type="module" src="/app.js"></script>',
    url="https://example.test",
    client=client,
)

print(page.select("#app").text)

Diagnostics

jsEasy is intentionally tolerant by default. A tracking script, analytics widget, or unsupported browser feature should not necessarily prevent scraping the rest of the DOM.

with Page.open("https://example.com") as page:
    print(page.logs)            # console.log/warn/error output
    print(page.script_errors)   # script exceptions collected during load
    print(page.resource_errors) # stylesheet/resource failures

Set raise_script_errors=True during development when you want the first script failure to raise immediately.

page = Page.from_html(html, raise_script_errors=True)

Browser API Coverage

jsEasy implements a pragmatic subset of browser APIs:

Area Supported
DOM Document, Element, Node, Text, DocumentFragment
Selection querySelector, querySelectorAll, matches, closest
Mutation appendChild, removeChild, insertBefore, innerHTML, textContent, basic MutationObserver
Events Event, CustomEvent, MouseEvent, KeyboardEvent, addEventListener, dispatchEvent
Runtime setTimeout, setInterval, requestAnimationFrame, Promise draining, performance.now
Network fetch, XMLHttpRequest, Request, Response, Headers, navigator.sendBeacon
State localStorage, sessionStorage, document.cookie, history, location
CSSOM document.styleSheets, CSSStyleSheet, CSSStyleRule, CSSStyleDeclaration, getComputedStyle
Modules classic scripts, type="module", simple static local imports
Utilities atob, btoa, console

See docs/api.md for details.

When To Use jsEasy

Use jsEasy when:

  • you need DOM extraction after simple or moderate JavaScript execution;
  • content is loaded via fetch() or XHR;
  • scripts manipulate the DOM but do not require layout;
  • you want fast startup in CLI jobs, tests, CI, workers, or small containers;
  • Playwright/Selenium feels too heavy for the target page.

Use a real browser when:

  • the site depends on layout metrics, canvas, WebGL, media, Shadow DOM, or complex framework hydration;
  • the target is fingerprint-sensitive;
  • you need browser DevTools, screenshots, or user interaction fidelity;
  • the page presents CAPTCHAs, payment walls, login walls, or other access controls.

Security And Ethics

jsEasy is intended for legitimate scraping, testing, research, and data extraction where you are allowed to access the content. Respect site terms, robots policies, rate limits, copyright, privacy, and access controls.

Do not use jsEasy to bypass CAPTCHAs, authentication, payment walls, or other security mechanisms. For security challenge pages, use a human-in-the-loop checkpoint.

Examples

See examples/:

Documentation

Development

python -m venv .venv
. .venv/bin/activate
pip install -e ".[dev]"
pytest
python -m build
twine check dist/*

Status

Alpha. jsEasy is useful today for small to medium JavaScript-enhanced scraping workflows, but the API may change before 1.0.

License

MIT

Metadata

Release files for jseasy 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 jseasy 0.1.0
File Size Uploaded Provenance Actions
jseasy-0.1.0.tar.gz 24.6 kB Without provenance Details for jseasy-0.1.0.tar.gz

Built distribution (wheel)

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

Total release size: 41.7 kB

Release files / jseasy-0.1.0.tar.gz

Download URL jseasy-0.1.0.tar.gz
Size 24.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b14f0d7369c586e92dde768675d8a63d29fdf9641850185d8947d51fc25fa0dd
BLAKE2b-256 checksum
How to use checksums
6891a0877760bdf2738b59b3825750e4bef0f84280533682d576b85f21c30428
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

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

Download URL jseasy-0.1.0-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2cb6d5293e6ce6fc5fcf1e0a00ebe8723cca30066bb665e678b3fbc104021f9c
BLAKE2b-256 checksum
How to use checksums
ce1a4e2f6aadc5d179b0db7efc70aee91d3ece6f6463faabec8d4fc362fbb2f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Security for jseasy 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