ECC
The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
npx ecc-install --profile fullThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
npx ecc-install --profile fullFair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
npx n8nAn open-source AI agent that brings the power of Gemini directly into your terminal.
npx @google/gemini-cliThese intent pages connect this repository to workflow-first and comparison-first discovery routes.
Shows active maintenance signals
Carries strong trust indicators from repository metadata
68899 GitHub stars recorded
Scrapling requires Python 3.10 or higher:
pip install scrapling
[!IMPORTANT] This installation only includes the parser engine and its dependencies, without any fetchers or commandline dependencies. So importing anything from
scrapling.fetchersorscrapling.spiders, like in the examples above, will raiseModuleNotFoundErrorwith this installation alone. If you are going to use any of the fetchers or spiders, install the fetchers' dependencies first as shown below.
If you are going to use any of the extra features below, the fetchers, or their classes, you will need to install fetchers' dependencies and their browser dependencies as follows:
pip install "scrapling[fetchers]"
scrapling install # normal install
scrapling install --force # force reinstall
This downloads all browsers, along with their system dependencies and fingerprint manipulation dependencies.
Or you can install them from the code instead of running a command like this:
from scrapling.cli import install
install([], standalone_mode=False) # normal install
install(["--force"], standalone_mode=False) # force reinstall
Extra features:
pip install "scrapling[ai]"
extract command):
pip install "scrapling[shell]"
pip install "scrapling[all]"
Remember that you need to install the browser dependencies with scrapling install after any of these extras (if you didn't already)
You can also install a Docker image with all extras and browsers with the following command from DockerHub:
docker pull pyd4vinci/scrapling
Or download it from the GitHub registry:
docker pull ghcr.io/d4vinci/scrapling:latest
This image is automatically built and pushed using GitHub Actions and the repository's main branch.
start_urls, async parse callbacks, and Request/Response objects.async for item in spider.stream() with real-time stats - ideal for UI, pipelines, and long-running crawls.robots_txt_obey flag that respects Disallow, Crawl-delay, and Request-rate directives with per-domain caching.parse() logic without re-hitting the target servers.result.items.to_json() / result.items.to_jsonl() respectively.FetcherDynamicFetcher class supporting Playwright's Chromium and Google's Chrome.StealthyFetcher and fingerprint spoofing. Can easily bypass all types of Cloudflare's Turnstile/Interstitial with automation.FetcherSession, StealthySession, and DynamicSession classes for cookie and state management across requests.ProxyRotator with cyclic or custom rotation strategies across all session types, plus per-request proxy overrides.Use multiple session types in a single spider:
from scrapling.spiders import Spider, Request, Response
from scrapling.fetchers import FetcherSession, AsyncStealthySession
class MultiSessionSpider(Spider):
name = "multi"
start_urls = ["https://example.com/"]
def configure_sessions(self, manager):
manager.add("fast", FetcherSession(impersonate="chrome"))
manager.add("stealth", AsyncStealthySession(headless=True), lazy=True)
async def parse(self, response: Response):
for link in response.css('a::attr(href)').getall():
# Route protected pages through the stealth session
if "protected" in link:
yield Request(link, sid="stealth")
else:
yield Request(link, sid="fast", callback=self.parse) # explicit callback
Pause and resume long crawls with checkpoints by running the spider like this:
QuotesSpider(crawldir="./crawl_data").start()
Press Ctrl+C to pause gracefully - progress is saved automatically. Later, when you start the spider again, pass the same crawldir, and it will resume from where it stopped.
from scrapling.fetchers import Fetcher
# Rich element selection and navigation
page = Fetcher.get('https://quotes.toscrape.com/')
# Get quotes with multiple selection methods
quotes = page.css('.quote') # CSS selector
quotes = page.xpath('//div[@class="quote"]') # XPath
quotes = page.find_all('div', {'class': 'quote'}) # BeautifulSoup-style
# Same as
quotes = page.find_all('div', class_='quote')
quotes = page.find_all(['div'], class_='quote')
quotes = page.find_all(class_='quote') # and so on...
# Find element by text content
quotes = page.find_by_text('quote', tag='div')
# Advanced navigation
quote_text = page.css('.quote')[0].css('.text::text').get()
quote_text = page.css('.quote').css('.text::text').getall() # Chained selectors
first_quote = page.css('.quote')[0]
author = first_quote.next_sibling.css('.author::text')
parent_container = first_quote.parent
# Element relationships and similarity
similar_elements = first_quote.find_similar()
below_elements = first_quote.below_elements()
You can use the parser right away if you don't want to fetch websites like below:
from scrapling.parser import Selector
page = Selector("<html>...</html>")
And it works precisely the same way!
import asyncio
from scrapling.fetchers import FetcherSession, AsyncStealthySession, AsyncDynamicSession
async with FetcherSession(http3=True) as session: # `FetcherSession` is context-aware and can work in both sync/async patterns
page1 = session.get('https://quotes.toscrape.com/')
page2 = session.get('https://quotes.toscrape.com/', impersonate='firefox135')
# Async session usage
async with AsyncStealthySession(max_pages=2) as session:
tasks = []
urls = ['https://example.com/page1', 'https://example.com/page2']
for url in urls:
task = session.fetch(url)
tasks.append(task)
print(session.get_pool_stats()) # Optional - The status of the browser tabs pool (busy/free/error)
results = await asyncio.gather(*tasks)
print(session.get_pool_stats())