Skip to main content

✈ mokr

Remote web browser automation.

About

mokr is a spirtual successor to pyppeteer, which it was originally forked from. However, mokr isn't meant to be a 1:1 drop-in replacement for it, and also doesn't seek to keep parity with puppeteer.

Some functionality has remained the same, but a lot has changed, too. Some elements have been based off of puppeteer proper and python-playwright, too.

mokr is named after MOCR, Nasa's Mission Operation Control Rooms that were used to control launches.

Quickstart

Run pip install mokr to install package.

Run mokr install to install browsers.

Run mokr scrape <url> to load the target page and dump contents to console.

Documentation

See the full documentation.

Usage

Launch a headless browser, navigate to a site, and dump the html to console.

import asyncio
from mokr import launch

async def main():
    async with launch() as browser:
        page = await browser.first_page()
        response = await page.goto("https://example.com")
        content = await response.content()
        print(content)
    
asyncio.run(main())

Launch a headful browser, hook some handlers to handle requests and responses, and navigate to the Wikipedia page for Python. Use the handlers to intercept the Python logo, make a new request for a picture of a python snake, and fulfill the original request with it.

import asyncio
from mokr import Request, Response, launch

async def main():
    snake_url = "https://upload.wikimedia.org/wikipedia/commons/3/32/Python_molurus_molurus_2.jpg"
    async with launch("chrome", headless=False) as browser:
        page = await browser.first_page()

        async def intercept_request(request: Request) -> Request | None:
            print(f"Intercepted request for: {request.url}")
            if request.url.endswith("Python-logo-notext.svg.png"):
                print("Getting a cute python picture to use as the new logo...")
                response = await page.fetch(snake_url)
                await request.fulfill(response)
            else:
                return request

        def log_response(response: Response) -> Request:
            print(f"Got {response.status} from: {response.url}")
        
        page.on("request", intercept_request)
        page.on("response", log_response)
        await page.goto("https://en.wikipedia.org/wiki/Python_(programming_language)")

asyncio.run(main())

Screenshot from running the above example. Screenshot from running the above example.

launch() returns shared protocol interfaces, also exported from mokr for convenient annotations.

from mokr import Browser, Page

async def inspect(page: Page) -> str:
    return await page.title()

async def first_page(browser: Browser) -> Page | None:
    return await browser.first_page()

However, if desired, direct objects could be imported from mokr.cdp or mokr.bidi.

Notable Changes from Pyppeteer

While forked from pyppeteer, there are some notable changes beyond reformating, refactoring, and restructuring! Including, but not limited to...

Changed:

  • The NetworkManager has been overhauled. The new Chrome implementation is based off of puppeteer heavily, but is not 1:1 with it. It uses the fetch domain instead of just the network domain.
  • Chrome request interception is enabled by default and can be disabled with Page.set_request_interception_enabled(False). Firefox request interception is enabled when a "request" route is registered and disabled when all are de-registered.
    • Browser.create has been replaced with Browser.ready and accepts no keyword arguments. This means a Browser can be instantied and target discovery postponed until .ready() is called.
    • The launch method is top-level and offers an async context manager to better handle graceful exits.

New:

  • Firefox support.
  • Pages expose a backend-specific fetch domain through Page.fetch_domain and the convenient Page.fetch shortcut.
  • Another new class, HttpDomain is available to send ad hoc requests via an httpx, HTTP2-enabled, client that syncs it's cookies with the parent Page and vice-versa.
  • Proxy support is baked-in, meaning you can pass a proxy string to mokr.launch directly.
  • New EventWaiter class; based off of pyppeteer.helper.waitForEvent method.

Removed:

  • Tracing has been removed.
  • The legacy element-handle querySelectorEval and querySelectorAllEval helpers have been removed.

Compared to...

Huge thanks are owed to the contributors of all the below projects, without them, this project would be quite different.

The disadvantages below are not a knock on any of these projects or their contributors.

Package Advantages Disadvantages
playwright-python
  • Well-maintained as owned by Microsoft.
  • Offers syncronous and asyncronous APIs.
  • Offers a fantastic request context.
  • Can be difficult to debug in Python.
    • Remote calls are made to the local playwright server, not to the browser directly.
    • APIs are generated, so digging into a method requires searching, or an actively running session, depending on your IDE.
  • Sync API is actually still running async code under-the-hood, which can lead to out-of-state browser pages and other unexpected behaviours.
puppeteer
  • Well-maintained, easily the largest Node.js browser automation library.
  • Working to support BiDi.
  • Written in Node.js, does not mesh with a Python ecosystem.
pyppeteer
  • Ported directly from puppeteer.
  • No longer maintained!
  • Does not use the fetch domain for request interception, resulting in unexpected behaviours with redirects in Chromium.
  • Not "pythonic" in some ways, with a fair amount of duplicated code and camelCase variables (likely due to being a port).

To Do

  • Explore decorating Page.wait_for_<x> methods with contextlib.asynccontextmanager so the syntax is more straightforward.

Release files for mokr 1.0.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 mokr 1.0.0
File Size Uploaded
mokr-1.0.0.tar.gz 296.5 kB Details

Built distribution (wheel)

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

Total release size: 474.2 kB

Release files / mokr-1.0.0.tar.gz

Download URL mokr-1.0.0.tar.gz
Size 296.5 kB
Tags Source
SHA-256 checksum
How to use checksums
300d949c45816051e597c7a2cacf0b806b8f47f9b63eca96dabb6687a34a5a61
BLAKE2b-256 checksum
How to use checksums
127a16211fb064d7ccd72af35b557f6cb034cc480eef04b066e3579c9a31e79f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.7

Release files / mokr-1.0.0-py3-none-any.whl

Download URL mokr-1.0.0-py3-none-any.whl
Size 177.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6866f4528ee71aebdd5f3688c6ef613d48d7a07b6134dbc500ffd926843ed941
BLAKE2b-256 checksum
How to use checksums
ed74b8aec8256859fb02690a416f496f94017ebed314f63320811ee04329c6b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.7

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.1.2

2 release files

0.1.1

2 release 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