Skip to main content

opencraw (Python)

Crawl with OpenCraw from Python, with no Node to install: Crawler downloads the driver's runtime for this platform once (a Node binary and the engine in one archive, checked against its SHA-256 and cached in ~/.cache/opencraw/driver/), starts it, and talks to it over its pipes. Standard library only; Python 3.12+.

from opencraw import Crawler

with Crawler(version="0.6.0") as crawler:

    @crawler.hook()
    def tidy(value, args, request):  # a hook the recipes call: plain Python
        return str(value).strip().lower()

    run = crawler.run(paths=["recipes/"])
    for record in run.records():  # as they are emitted
        print(record)
    print(run.report()["records"])
  • crawler.run(...) takes the driver's run options (out, only, dryRun, parallel, access, ...); run.records() and run.events() yield as it crawls, run.report() waits for the end, run.cancel() stops it.
  • crawler.work(next_item, ...) is worker mode: next_item() returns each window's item ({"id": ..., "vars": {...}}), or None to end.
  • @crawler.hook(), @crawler.captcha_solver(), @crawler.access_plugin() register Python functions by name; raising fails that one call (CalloutError for a plain message), never the run.
  • crawler.validate, probe, diff, export answer as the driver does.
  • AsyncCrawler is the asyncio twin: async with AsyncCrawler(version=...) as crawler, await crawler.run(...), async for record in run.records(), and hooks, plugins and next_item may be async def.
  • OPENCRAW_DRIVER=<folder> uses an unpacked runtime; OPENCRAW_DRIVER_OFFLINE=1 never downloads; command= starts any other driver (["node", "path/to/main.esm.js"]).

The rest of this page is the older way, which stays: build recipes in Python and run the opencraw CLI once per call (npm i -g @opencraw/cli, or OPENCRAW_CLI set to the command that starts it; with neither, npx @opencraw/cli is used).

A recipe stays a plain dict in the engine's own shape, so the recipe guide (docs/recipes/authoring.md) is the reference for every key. The constructors only save typing, and the engine, not this package, says what is valid.

from opencraw import input_recipe, output_recipe, run, step, validate

product = output_recipe(
    "product",
    {
        "url": {"type": "url", "required": True, "key": True},
        "title": {"type": "string", "required": True},
    },
)

shops = [
    input_recipe(
        f"shop-{name}",
        "product",
        "web",
        [url],
        [
            step("goto", url="{{start.url}}"),
            step("extract", id="title", selector=selector, kind="css", take="text"),
            step("emit"),
        ],
        {"url": {"from": "start.url"}, "title": {"from": "title"}},
    )
    for name, url, selector in [
        ("a", "https://a.example", "h1"),
        ("b", "https://b.example", ".name"),
    ]
]

result = validate(
    [product, *shops]
)  # Validation(ok, output): problems are data, not exceptions
records = run(
    [product, *shops], "--only", "shop-a"
)  # records as dicts; OpenCrawError if the crawl fails

Reserved words: pass a step's as or from through **{"as": "json"}.

write_recipes(folder, recipes) writes <id>.input.json and <id>.output.json for the CLI, the Studio and the MCP server.

A hook written in Python

A recipe's hook ({ "op": "hook", "name": "slug" }) can be a Python script. The hooks module registers it with commandHook (@opencraw/core), and the script is a few lines:

# hooks/slug.py
import re
from opencraw import serve_hook

serve_hook(
    lambda value, args, request: re.sub(r"[^a-z0-9]+", "-", str(value).lower()).strip(
        "-"
    )
)

serve_hook(handler) reads the request from stdin, calls handler(input, args, request) (input is None for a hook step; request carries context and idempotencyKey) and writes the answer to stdout. What the handler returns is the output; an exception becomes an error answer with its message, so the step fails with that reason. A handler whose work takes longer raises HookPending(retry_after_ms): OpenCraw runs the script again after that wait, with the same idempotencyKey, until it returns or the hook's maxWaitMs passes, so the script starts the work once and looks it up by that key on later runs. The request and answer shapes are in packages/core/schemas/callout-*.schema.json.

Not in this package: a Python engine and generated typed models. The crawl itself always runs in the OpenCraw CLI. It is published to PyPI as opencraw: mnci ci release versions and publishes what sits under python-packages/.

Development

smith run test -p opencraw (pytest; the CLI integration tests use this repository's built CLI and skip without it), smith run lint -p opencraw (Ruff), smith run typecheck -p opencraw (mypy strict).

Layout. One slice package per outcome: recipe_building/ (recipes in Python, written to files), hook_serving/ (hooks served as command callouts), cli_bridge/ (the CLI, run once per call), driver_runtime/ (the runtime's download and cache), driver_channel/ (JSON-RPC over the driver's pipes) and crawl_session/ (Crawler and Run). Its tests replay the driver's conformance transcripts against the repository's built driver.

Metadata

Release files for opencraw 0.3.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 opencraw 0.3.0
File Size Uploaded Provenance
opencraw-0.3.0.tar.gz 26.2 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 52.6 kB

Release files / opencraw-0.3.0.tar.gz

Download URL opencraw-0.3.0.tar.gz
Size 26.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ce37216fb12192a9e0fb497ca212e5c478db8e2b61b803aa1f1dfbfe0b8d0ff1
BLAKE2b-256 checksum
How to use checksums
cfe75095ac64676405d92d98bc829babf0c2162d563f72b18de904917f05f6c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / opencraw-0.3.0-py3-none-any.whl

Download URL opencraw-0.3.0-py3-none-any.whl
Size 26.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1bfeae5feb74497607cb920f5449bf2b27864ec73f2846a3181ca16e81f40573
BLAKE2b-256 checksum
How to use checksums
3b6ecf1160e9dbdef23486fb13fc7024f61f17445883abc7c20813aea53ec762
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Security for opencraw 0.3.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.

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