Skip to main content

""" Qrawlix — High-Performance Asynchronous Web Scraping & Asset Retrieval

Qrawlix is a research-oriented Python library for fetching web pages, extracting structured data, and downloading binary assets (images, documents, archives) at scale. It is built on top of curl_cffi, which provides browser-grade TLS fingerprint emulation — allowing requests to appear as though they originate from standard browsers (Chrome, Firefox, Safari, Edge).

Key capabilities

  • TLS fingerprint rotation — automatic per-request browser identity cycling to improve compatibility with diverse server configurations.
  • Concurrent scraping — fetch dozens of pages simultaneously with configurable parallelism and built-in rate-limiting.
  • Concurrent asset downloading — download hundreds of images or files in parallel, with per-directory organisation.
  • Structured data extraction — declarative CSS-selector-based rules for pulling titles, prices, links, and other fields from HTML.
  • Multi-strategy request routing — automatic fallback through secondary channels when a direct connection is unavailable.
  • Parallel engine — high-level orchestrator that handles the full pipeline (scrape → extract → download → organise) in one call.

Installation

::

pip install qrawlix

Or from source::

git clone https://github.com/YOUR_USER/qrawlix.git
cd qrawlix
pip install .

Quick start

Scrape a single page and extract the title::

import asyncio
from qrawlix import QrawlixClient

async def main():
    client = QrawlixClient()
    result = await client.scrape("https://example.com")
    if result["success"]:
        title = result["parser"].text("h1")
        print(f"Page title: {title}")

asyncio.run(main())

Scrape multiple pages and download all discovered images in one call::

from qrawlix.parallel import scrape_pages

results = asyncio.run(scrape_pages([
    "https://site-a.com/gallery",
    "https://site-b.com/chapter/42",
]))

print(f"{results['succeeded']}/{results['targets']} sites processed")
print(f"{results['images_downloaded']} images downloaded")

Output is automatically organised::

qrawlix_output/
├── summary.json
├── site-a.com/
│   ├── page.html
│   ├── data.json
│   └── images/
│       ├── photo_001.jpg
│       └── photo_002.png
└── site-b.com/
    ├── page.html
    ├── data.json
    └── images/
        └── ...

Core API

QrawlixClient — async HTTP client


::

    from qrawlix import QrawlixClient

    client = QrawlixClient(
        max_concurrency=10,   # max simultaneous requests
        timeout=15.0,         # per-request timeout (seconds)
        retries=1,            # retry attempts on failure
    )

    # Single URL
    result = await client.scrape("https://example.com")
    # result["success"]         — bool
    # result["html"]            — raw response text
    # result["parser"]          — ContentParser instance
    # result["elapsed_ms"]      — request latency
    # result["status_code"]     — HTTP status

    # Structured extraction
    result = await client.scrape("https://example.com", rules={
        "title": {"selector": "h1", "type": "text"},
        "links": {"selector": "a", "type": "attr_all", "attr": "href"},
    })
    # result["extracted_data"]  — dict with extracted fields

    # Multiple URLs concurrently
    batch = await client.scrape_many([
        "https://site-a.com",
        "https://site-b.com",
    ])
    # batch["successful"]       — count of successful fetches
    # batch["results"]          — list of per-URL result dicts


**ContentParser** — HTML / markdown parser

::

parser = result["parser"]          # obtained from QrawlixClient.scrape()

parser.text("h1")                  # first match inner text
parser.text_all("p")               # all match inner texts
parser.attribute("a", "href")      # first match attribute
parser.attribute_all("img", "src") # all match attributes

# Declarative extraction
data = parser.extract_by_rules({
    "title":   {"selector": "h1",              "type": "text"},
    "price":   {"selector": ".price",          "type": "text"},
    "image":   {"selector": "img.product",     "type": "attr",  "attr": "src"},
    "gallery": {"selector": ".gallery img",    "type": "attr_all", "attr": "src"},
})

# List extraction (repeating containers)
items = parser.extract_list("div.product-card", {
    "name":  {"selector": "h2",   "type": "text"},
    "link":  {"selector": "a",    "type": "attr", "attr": "href"},
})

AssetPipeline — concurrent binary downloader


::

    from qrawlix import AssetPipeline

    pipeline = AssetPipeline(
        output_dir="downloads",
        concurrency=20,          # max simultaneous downloads
    )
    dl = await pipeline.download_all([
        "https://example.com/photo1.jpg",
        "https://example.com/photo2.png",
    ])
    # dl["total_downloaded"]   — success count
    # dl["total_bytes"]        — total size downloaded
    # dl["results"]            — per-asset status dicts


**ParallelEngine** — full pipeline orchestrator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

    from qrawlix import ParallelEngine

    engine = ParallelEngine(
        scrape_concurrency=8,    # concurrent page fetches
        download_concurrency=24, # concurrent asset downloads
    )
    summary = await engine.run([
        "https://site-a.com/gallery",
        "https://site-b.com/chapter/1",
    ], output_root="my_output")

    # One-liner equivalent:
    from qrawlix.parallel import scrape_pages
    summary = await scrape_pages(urls, output="my_output")


Configuration
-------------
Concurrency levels are automatically derived from system CPU count
when not explicitly set.  The default heuristic is ``max(4, min(cpu*2, 32))``.

Custom image selectors can be passed to the parallel engine for
domain-specific extraction::

    engine = ParallelEngine(image_selectors={
        "my-site.com": [".custom-gallery img", "img.content"],
    })

Important notes
---------------
- This library is provided for **research and educational purposes**.
  Users are responsible for complying with the terms of service of any
  website they interact with.
- Qrawlix uses public content-reader proxies and translation services
  as secondary request channels when a direct connection is unavailable.
  These are best-effort fallbacks and may not work for all endpoints.
- The library does **not** execute JavaScript.  Pages that rely entirely
  on client-side rendering (e.g., Google Images, React SPAs) will return
  limited content from the initial HTML payload.

License
-------
MIT License — see the LICENSE file for details.
"""

Metadata

Release files for qrawlix 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 qrawlix 1.0.0
File Size Uploaded Provenance Actions
qrawlix-1.0.0.tar.gz 16.1 kB Without provenance Details for qrawlix-1.0.0.tar.gz

Built distribution (wheel)

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

Total release size: 36.1 kB

Release files / qrawlix-1.0.0.tar.gz

Download URL qrawlix-1.0.0.tar.gz
Size 16.1 kB
Tags Source
SHA-256 checksum
How to use checksums
508c6ae13fe4e34f591a88cbd4cd89867e7d680f3c1eeed9326a0702c6421b5a
BLAKE2b-256 checksum
How to use checksums
33b00ca89ad0ba0ed935fb1bc29dfa53b6739bbf785c5a268d00132a50f01352
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.10

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

Download URL qrawlix-1.0.0-py3-none-any.whl
Size 20.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4c7f008a92b1d198301285011de7e2f87d5a92ccf3a694d318771c2827ce146e
BLAKE2b-256 checksum
How to use checksums
4da7515e42c6bae4f2d98b0bbef4b249bf2d83a1e1dcab1401f9bb86796cb23c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.10

Security for qrawlix 1.0.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

1.0.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