Skip to main content

parsek-cdp-server

The server side of parsek-cdp: launching, supervising and proxying a browser, plus the server-side feature producers that digest raw CDP into Parsek.* events.

A separate distribution from parsek-cdp (the client); installed via pip install parsek-cdp[server]. It depends on parsek-cdp and reuses its shared layers wholesale — the generated cdp protocol, the core primitives (CDPConnection, Target, DataType) and the parsek contract — adding only what must live next to the browser.

Документация на русском: README.ru.md.

Running

import asyncio
from parsek_cdp_server import ParsekServer
from parsek_cdp.features import RequestListener


async def main():
    server = ParsekServer(idle_timeout=300,    # a browser self-closes after 5 min idle
                          inactivity_timeout=600)  # ...or 10 min without a client message
    server.register_feature(RequestListener)   # make the feature selectable via ?feature=
    await server.start("127.0.0.1", 9333)
    try:
        await asyncio.Event().wait()           # keep the server alive
    finally:
        await server.stop()


asyncio.run(main())

A client connects to this server with Browser.get_distant_browser("http://127.0.0.1:9333", ...) — the API is identical to a local browser (see the client README). Every POST /browsers spins up its own supervised browser. Two timeouts let a browser close itself, each settable on the server or per-request in the body (omit, or pass a non-positive value, to disable):

  • idle_timeout — seconds with no connection at all;
  • inactivity_timeout — seconds without a single client message, connected or not, which is what catches a client that connected and then went quiet. Only what the client sends counts, so keep it above the longest the client may legitimately spend waiting on the browser (a slow navigation, a wait_for) — waiting sends nothing and the clock keeps running.

Either way the client is told why: the control channel gets a Parsek.browserStateChanged with state=closed and the reason before the browser goes down.

Where the browser comes from

launcher finds a Chromium-compatible binary on its own. By default it walks the usual install paths; the PARSEK_CHROMES_PATH env var (directories separated by os.pathsep, like PATH) overrides the search — each launch picks a random one of the browsers it finds (a cheap way to spread load and fingerprints). An explicit path and flags go through LaunchOptions (executable, headless, extra_args, ...) — either on the server or per-request in the POST /browsers body.

Modules

Module Responsibility
launcher Start Chrome/Chromium, wait for the DevTools endpoint, read the browser-level websocket from /json/version.
supervisor Browser lifecycle: health, crash detection, restart, idle/inactivity shutdown, and Parsek.browserStateChanged broadcast.
proxy Accept client websockets and bridge each 1:1 to a Chrome target; host feature producers and serve the Parsek.* surface.
metrics Prometheus exposition at /metrics.
reaper A separate subprocess that kills leaked (zombie/orphaned) parsek-launched Chrome processes.

Connection model

As in the client, every target gets its own websocket (no sessionId multiplexing). One parsing flow = one browser_context; pages within it each connect over their own websocket. The proxy bridges a page's client socket to the corresponding Chrome socket 1:1.

Endpoints

HTTP POST /browsers                                   create a task -> browser_uuid
HTTP GET  /metrics                                    Prometheus metrics
ws        /cdp/{browser_uuid}/control                 browser-level pipe + Parsek.browserStateChanged
ws        /cdp/{browser_uuid}/page/{target_id}        CDP proxy (per-page) + Parsek.*

Responses

POST /browsers returns 200 application/json — the browser id and its control-channel endpoint:

{
  "browserUuid": "1f2ab34cd56e78f9...",
  "wsUrl": "ws://127.0.0.1:9333/cdp/1f2ab34cd56e78f9.../control"
}

GET /metrics returns 200 text/plain in the Prometheus text exposition format (see the metrics table below).

The websocket endpoints (/control and /page/{target_id}) respond with a websocket upgrade; for an unknown browser_uuid the socket is closed immediately with code 4040 ("unknown browser").

The control channel is a raw pipe to the browser endpoint plus the Parsek.browserStateChanged broadcast, so a parser learns when its browser crashed/restarted instead of hanging; browser-level CDP (creating/disposing contexts, etc.) passes straight through it.

Features

The server is pure passthrough by default (ParsekServer has no features argument). Feature producers are added with register_feature(...) and selected per browser with POST /browsers?feature=<name>. On a page pipe a producer digests the raw CDP burst into a couple of aggregated Parsek.* events and suppresses the raw events it consumed; ?raw=1 opts out of suppression.

Metrics (GET /metrics)

Metric Type What
parsek_browsers gauge number of supervised browsers
parsek_targets{browser,type} gauge targets per browser, by type
parsek_nested_targets{browser} gauge nested targets (with a parentId)
parsek_browser_cpu_percent{browser} gauge CPU% of the browser process tree
parsek_browser_memory_bytes{browser} gauge RSS of the browser process tree
parsek_cdp_events_total{browser,target,domain} counter CDP events by target and domain

Targets and CPU/RAM are sampled by a background task (polling /json/list + psutil), so a scrape only reads cached values and does not block the event loop. The event counter is labelled by target id — series for dead targets are pruned on every snapshot.

Zombie reaper

On start the server spawns a separate subprocess (reaper) that periodically scans for Chrome processes we launched (recognised by the --user-data-dir=…parsek-cdp-… marker) that became zombies or orphans (ppid == 1), and kills them along with their subtree. Controlled by ParsekServer(reap_zombies=..., reap_interval=...); it can also be run by hand: python -m parsek_cdp_server.reaper --once.

Notes

The Parsek.* contract lives in the client distribution (parsek_cdp.parsek) and is shared by both sides.

License

Apache-2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

parsek_cdp_server-0.1.8.tar.gz (32.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

parsek_cdp_server-0.1.8-py3-none-any.whl (34.0 kB view details)

Uploaded Python 3

File details

Details for the file parsek_cdp_server-0.1.8.tar.gz.

File metadata

  • Download URL: parsek_cdp_server-0.1.8.tar.gz
  • Upload date:
  • Size: 32.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for parsek_cdp_server-0.1.8.tar.gz
Algorithm Hash digest
SHA256 68da4f302efc6b15f1f3c0a3196c643c19673d4b750bf8417032a44a9ac061eb
MD5 bc63307f1f9eef7891c0fc8794129a1a
BLAKE2b-256 a262ac44060910e7761ff983a500b95409a0ec88761331d2708104bd5650e04c

See more details on using hashes here.

Provenance

The following attestation bundles were made for parsek_cdp_server-0.1.8.tar.gz:

Publisher: publish.yml on xa1era/parsek-cdp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file parsek_cdp_server-0.1.8-py3-none-any.whl.

File metadata

File hashes

Hashes for parsek_cdp_server-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 de2c7a5a8240e8f1af968038a17817805532847b7c4f463edcd819a30a50cabf
MD5 5bd6038f9d16a628520b6fcbb62ea558
BLAKE2b-256 20214c78bce1562722de7116c3bed938e42f82f1e260c101ca0ed5899a05633d

See more details on using hashes here.

Provenance

The following attestation bundles were made for parsek_cdp_server-0.1.8-py3-none-any.whl:

Publisher: publish.yml on xa1era/parsek-cdp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.9

2 files

This release

0.1.8 This release

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

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