Skip to main content

High-performance async web scraping and asset retrieval with TLS fingerprint emulation and concurrent processing.

Project description

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

Project details


Download files

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

Source Distribution

qrawlix-1.0.0.tar.gz (16.1 kB view details)

Uploaded Source

Built Distribution

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

qrawlix-1.0.0-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file qrawlix-1.0.0.tar.gz.

File metadata

  • Download URL: qrawlix-1.0.0.tar.gz
  • Upload date:
  • Size: 16.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for qrawlix-1.0.0.tar.gz
Algorithm Hash digest
SHA256 508c6ae13fe4e34f591a88cbd4cd89867e7d680f3c1eeed9326a0702c6421b5a
MD5 5a83f4a912fdc1223a98074c63c06657
BLAKE2b-256 33b00ca89ad0ba0ed935fb1bc29dfa53b6739bbf785c5a268d00132a50f01352

See more details on using hashes here.

File details

Details for the file qrawlix-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: qrawlix-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for qrawlix-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4c7f008a92b1d198301285011de7e2f87d5a92ccf3a694d318771c2827ce146e
MD5 ce7da04d4239f5e1bfef1b80b5fc0c25
BLAKE2b-256 4da7515e42c6bae4f2d98b0bbef4b249bf2d83a1e1dcab1401f9bb86796cb23c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page