mudfish
Python bindings for Mudfish Crawler, a Rust-native web crawling and intelligence engine.
This package wraps the Rust crawl engine directly (via PyO3) — there is no subprocess or network hop to a separate server. It exposes a typed, Pythonic API over the engine; the Rust project itself is at an early stage (see the main repo's ROADMAP_HONEST.md for full status).
Install
pip install mudfish
Usage
import mudfish
result = mudfish.crawl("https://example.com", depth=2, concurrency=30)
print(result.stats.urls_fetched, result.stats.duration_secs)
for page in result: # CrawlResult is iterable over its pages
print(page.status_code, page.url, page.title)
crawl() blocks until the crawl finishes (or hits max_urls / max_duration_secs) and returns a typed CrawlResult:
| Object | Fields |
|---|---|
CrawlResult |
crawl_id, pages: list[Page], errors: list[CrawlError], stats: CrawlStats — iterable over pages, len() is the page count, plus ok_pages |
Page |
url, final_url, status_code, content_type, depth, metadata: PageMetadata, links: list[Link], body_bytes, fetched_via: FetchMethod — helpers title, is_ok |
PageMetadata |
title, meta_description, canonical |
Link |
url, anchor_text, rel |
CrawlError |
url, message |
CrawlStats |
urls_discovered, urls_fetched, urls_skipped, errors, bytes_downloaded, duration_ms — helpers duration_secs, throughput_per_sec |
FetchMethod |
FetchMethod.HTTP ("Http") / FetchMethod.BROWSER ("Browser") |
Every typed object also supports the raw engine dict shape (result["stats"], page["status_code"], page.to_dict()), so code written against earlier versions keeps working. If you only want the raw dict, use mudfish.crawl_dict(...).
Async
crawl_async() runs the blocking engine on a worker thread, so it composes with asyncio and several crawls can run concurrently:
import asyncio
import mudfish
async def main() -> None:
results = await asyncio.gather(
mudfish.crawl_async("https://a.example"),
mudfish.crawl_async("https://b.example"),
)
for result in results:
print(result.stats.urls_fetched)
asyncio.run(main())
Reusable configuration
crawler = mudfish.Crawler(depth=1, concurrency=10)
home = crawler.crawl("https://example.com")
blog = await crawler.crawl_async("https://example.com/blog")
Errors
try:
mudfish.crawl("not-a-url")
except mudfish.InvalidURLError: # also a ValueError
...
except mudfish.CrawlFailedError: # also a RuntimeError
...
Everything derives from mudfish.MudfishError.
Options
All keyword-only, matching the CLI's flags (also available as the CrawlOptions dataclass):
| Parameter | Default | Meaning |
|---|---|---|
depth |
3 |
Max link-following depth |
concurrency |
50 |
Max requests in flight, whole crawl |
per_host_concurrency |
4 |
Max requests in flight per host |
same_domain |
True |
Restrict crawl to the seed's resolved domain |
max_urls |
10000 |
Hard cap on URLs fetched |
max_duration_secs |
None |
Optional wall-clock budget |
timeout_secs |
30 |
Per-request timeout |
request_delay_ms |
0 |
Min delay between requests to the same host |
max_response_bytes |
20 MB |
Per-response size cap |
respect_robots |
True |
Honor robots.txt |
allow_private_networks |
False |
Permit loopback/private targets (internal use only) |
render_js |
False |
Re-render JS-dependent pages in headless Chrome (needs Chrome on the host; much slower) |
render_settle_ms |
2000 |
When render_js is on, settle time before capturing a rendered page |
proxies |
() |
Proxy URLs to route requests through; supply several to rotate a pool |
proxy_rotate_after |
50 |
Requests through a proxy before rotating to the next |
chrome_path |
None |
Explicit Chrome/chrome-headless-shell binary for render_js; defaults to the bundled binary, then host autodetection |
Limitations
- Chrome is not bundled in the PyPI wheel. With
render_js=Truethe engine drives a headless Chrome that must already be installed on the host; if it cannot be launched, the crawl logs a warning and falls back to static HTML. A separate bundled wheel that ships achrome-headless-shell(sorender_jsworks with no host install) is attached to the GitHub release for each version — look for the asset namedmudfish-<version>+chrome-*.whl. - Single-platform wheels initially. Built and published from the maintainer's machine, not a cross-platform CI matrix yet — check PyPI for which platforms have wheels; others will need a Rust toolchain to build from source.
Bundled-Chrome builds
To build the bundled wheel locally:
python3 tools/fetch_chrome_headless_shell.py # pins + downloads chrome-headless-shell
cd crates/python && maturin build --release
The binary is fetched (never committed) into python/mudfish/_chrome/, the wheel picks it up automatically, and at runtime mudfish.bundled_chrome_path() returns it (overridable with MUDFISH_CHROME).
License
Apache-2.0.
Metadata
Release files for mudfish 0.2.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| mudfish-0.2.5.tar.gz | 94.9 kB | Without provenance | Details for mudfish-0.2.5.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mudfish-0.2.5-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details for mudfish-0.2.5-cp39-abi3-macosx_11_0_arm64.whl |
Total release size: 104.2 MB
Release files / mudfish-0.2.5.tar.gz
| Download URL | mudfish-0.2.5.tar.gz |
|---|---|
| Size | 94.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b29528607e51184152833816d3145e0748269fe331a0538db92249814072e39
|
|
BLAKE2b-256 checksum How to use checksums |
a5d096a59491f276e00058a834fbeffacb66229d0782516746581609c57d2ae9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / mudfish-0.2.5-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | mudfish-0.2.5-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 104.1 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
fa9883add7f4cab1740b0156529a26a5d42156c9fe20280adfee70673a06c2bb
|
|
BLAKE2b-256 checksum How to use checksums |
a991c12477898a22d0041cf4571d0dda6fd93b1bb9d7bcc8e277533a1bcabd5d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Security for mudfish 0.2.5
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.