""" 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.