API Reference Book
How to Read This Book
This reference book was generated from the publicly accessible documentation
at https://docs.crawl4ai.com on 2026-08-11. It is a structured,
self-contained snapshot suitable for offline reference and AI ingestion.
Where to start
- Table of Contents — the right pane (HTML) or page 2 (PDF) shows every chapter and sub-section. Each entry links to its anchor.
- Chapter introduction — each chapter opens with a one-paragraph summary explaining its scope.
- Code blocks — syntax-highlighted using Pygments; copy-pasteable.
- Search — use your reader's full-text search (Ctrl-F) for any symbol or word.
Conventions
- Inline code:
identifier. - Parameter descriptions use italics; required vs optional is called out explicitly.
- Cross-references (e.g. see §4.2) are clickable in the HTML version.
Introduction & Overview
🚀🤖 Crawl4AI: Open-Source LLM-Friendly Web Crawler & Scraper
Crawl4AI is an open-source, LLM-friendly web crawler and scraper that produces clean Markdown, supports structured extraction, advanced browser control, and high-performance parallel crawling. This…
🚀 Crawl4AI Cloud API — Closed Beta (Launching Soon)
Reliable, large-scale web extraction, now built to be drastically more cost-effective than any of the existing solutions.
👉 **Apply here for early…
Crawl4AI: Open-Source LLM-Friendly Web Crawler & Scraper
Crawl4AI is the #1 trending GitHub repository, actively maintained by a vibrant community. It delivers blazing-fast, AI-ready web crawling tailored for large language models, AI agents, and data…
🆕 AI Assistant Skill Now Available!
🤖 Crawl4AI Skill for Claude & AI Assistants
Supercharge your AI coding assistant with complete Crawl4AI knowledge! Download our comprehensive skill package that includes:
- 📚 Complete SDK reference (23K+ words)
- 🚀 Ready-to-use extraction…
🎯 New: Adaptive Web Crawling
Crawl4AI now features intelligent adaptive crawling that knows when to stop! Using advanced information foraging algorithms, it determines when sufficient information has been gathered to answer your…
Quick Start
Here's a quick example to show you how easy it is to use Crawl4AI with its asynchronous capabilities:
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
# Create an instance of AsyncWebCrawler
async with AsyncWebCrawler() as crawler:
# Run the crawler on a URL…
Video Tutorial
What Does Crawl4AI Do?
Crawl4AI is a feature-rich crawler and scraper that aims to:
- Generate Clean Markdown: Perfect for RAG pipelines or direct ingestion into LLMs.
- Structured Extraction: Parse repeated…
Documentation Structure
To help you get started, we’ve organized our docs into clear sections:
- Setup & Installation: Basic instructions to install Crawl4AI via pip or Docker.
- Quick Start: A hands-on…
How You Can Support
- Star & Fork: If you find Crawl4AI helpful, star the repo on GitHub or fork it to add your own features.
- File Issues: Encounter a bug or missing feature? Let us know by filing an issue, so…
Quick Links
Home - Crawl4AI Documentation (v0.9.x)
Landing page and overview for Crawl4AI, an open-source, LLM-friendly web crawler and scraper. It introduces the project's mission, key capabilities, quick-start example, documentation structure, and…
🚀🤖 Crawl4AI: Open-Source LLM-Friendly Web Crawler & Scraper
🚀 Crawl4AI Cloud API — Closed Beta (Launching Soon)
Reliable, large-scale web extraction, now built to be drastically more cost-effective than any of the existing solutions.
👉 Apply here for early…
🆕 AI Assistant Skill Now Available!
🤖 Crawl4AI Skill for Claude & AI Assistants
Supercharge your AI coding assistant with complete Crawl4AI knowledge! Download our comprehensive skill package that includes:
- 📚 Complete SDK reference…
🎯 New: Adaptive Web Crawling
Crawl4AI now features intelligent adaptive crawling that knows when to stop! Using advanced information foraging algorithms, it determines when sufficient information has been gathered to answer your…
Quick Start
Here's a quick example to show you how easy it is to use Crawl4AI with its asynchronous capabilities:
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
# Create an instance of AsyncWebCrawler
async with AsyncWebCrawler() as crawler:
# Run the crawler on a URL…
Video Tutorial
What Does Crawl4AI Do?
Crawl4AI is a feature-rich crawler and scraper that aims to:
- Generate Clean Markdown: Perfect for RAG pipelines or direct ingestion into LLMs.
- Structured Extraction: Parse repeated patterns…
Documentation Structure
To help you get started, we’ve organized our docs into clear sections:
- Setup & Installation — Basic instructions to install Crawl4AI via pip or Docker.
- Quick Start — A hands-on introduction…
How You Can Support
- Star & Fork: If you find Crawl4AI helpful, star the repo on GitHub or fork it to add your own features.
- File Issues: Encounter a bug or missing feature? Let us know by filing an issue, so we can…
Quick Links
- GitHub Repo
- Installation Guide
- Quick Start
- API Reference
- Changelog
Thank you for joining me on this journey. Let’s keep building an open, democratic approach to data extraction and AI…
Overview of Some Important Advanced Features
A guide to advanced Crawl4AI features including proxy usage, PDF/screenshot capture, SSL certificates, custom headers, session persistence, robots.txt compliance, and anti-bot techniques.
Overview of Some Important Advanced Features
Crawl4AI offers multiple power-user features that go beyond simple crawling. This tutorial covers:
-
Proxy Usage
-
Capturing PDFs & Screenshots
-
Handling SSL Certificates
4.…
1. Proxy Usage
If you need to route your crawl traffic through a proxy—whether for IP rotation, geo-testing, or privacy—Crawl4AI supports it via BrowserConfig.proxy_config.
Key Points
proxy_config…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
browser_cfg = BrowserConfig(
proxy_config={
"server":…
2. Capturing PDFs & Screenshots
Sometimes you need a visual record of a page or a PDF “printout.” Crawl4AI can do both in one pass:
Why PDF + Screenshot?
- Large or complex pages can be slow or error-prone with “traditional”…
import os, asyncio
from base64 import b64decode
from crawl4ai import AsyncWebCrawler, CacheMode, CrawlerRunConfig
async def main():
run_config = CrawlerRunConfig(…
3. Handling SSL Certificates
If you need to verify or export a site’s SSL certificate—for compliance, debugging, or data analysis—Crawl4AI can fetch it during the crawl:
Key Points
fetch_ssl_certificate=True…
import asyncio, os
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
tmp_dir = os.path.join(os.getcwd(), "tmp")
os.makedirs(tmp_dir, exist_ok=True)…
4. Custom Headers
Sometimes you need to set custom headers (e.g., language preferences, authentication tokens, or specialized user-agent strings). You can do this in multiple ways:
Notes
- Some sites may react…
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
# Option 1: Set headers at the crawler strategy level
crawler1 = AsyncWebCrawler(
# The underlying strategy can…
5. Session Persistence & Local Storage
Crawl4AI can preserve cookies and localStorage so you can continue where you left off—ideal for logging into sites or skipping repeated auth flows.
5.1 `storage_state`
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
storage_dict = {
"cookies": [
{
"name": "session",
"value": "abcd1234",…
5.2 Exporting & Reusing State
You can sign in once, export the browser context, and reuse it later—without re-entering credentials.
await context.storage_state(path="my_storage.json"): Exports cookies, localStorage, etc.…
6. Robots.txt Compliance
Crawl4AI supports respecting robots.txt rules with efficient caching:
Key Points
-
Robots.txt files are cached locally for efficiency
-
Cache is stored in…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
# Enable robots.txt checking in config
config = CrawlerRunConfig(
check_robots_txt=True #…
Putting It All Together
Here’s a snippet that combines multiple “advanced” features (proxy, PDF, screenshot, SSL, custom headers, and session reuse) into one run. Normally, you’d tailor each setting to your project’s needs.
import os, asyncio
from base64 import b64decode
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
async def main():
# 1. Browser config with proxy + headless…
7. Anti-Bot Features (Stealth Mode & Undetected Browser)
Crawl4AI provides two powerful features to bypass bot detection:
7.1 Stealth Mode
Stealth mode uses playwright-stealth to modify browser fingerprints and behaviors. Enable it with a simple flag:
When to use: Sites with basic bot detection (checking navigator.webdriver,…
browser_config = BrowserConfig(
enable_stealth=True, # Activates stealth mode
headless=False
)
7.2 Undetected Browser
For advanced bot detection, use the undetected browser adapter:
When to use: Sites with sophisticated bot detection (Cloudflare, DataDome, etc.)
from crawl4ai import UndetectedAdapter
from crawl4ai.async_crawler_strategy import AsyncPlaywrightCrawlerStrategy
# Create undetected adapter
adapter = UndetectedAdapter()
strategy =…
7.3 Combining Both
For maximum evasion, combine stealth mode with undetected browser:
browser_config = BrowserConfig(
enable_stealth=True, # Enable stealth
headless=False
)
adapter = UndetectedAdapter() # Use undetected browser
Choosing the Right Approach
| Detection Level | Recommended Approach |
|---|---|
| No protection | Regular browser |
| Basic checks | Regular + Stealth mode |
| Advanced protection | Undetected browser |
| Maximum evasion | … |
Conclusion & Next Steps
You've now explored several advanced features:
-
Proxy Usage
-
PDF & Screenshot capturing for large or critical pages
-
SSL Certificate retrieval & exporting
-
Custom Headers…
Getting Started
Installation 💻 - Crawl4AI Documentation (v0.9.x)
Extraction fallback content.
Installation 💻 - Crawl4AI Documentation (v0.9.x)
Installation 💻
Crawl4AI offers flexible installation options to suit various use cases. You can install it as a Python package, use it with Docker, or run it as a local server.
Option 1: Python Package Installation (Recommended)
Crawl4AI is now available on PyPI, making installation easier than ever. Choose the option that best fits your needs:
Basic Installation
For basic web crawling and scraping tasks:
Installation with PyTorch
For advanced text clustering (includes CosineSimilarity cluster strategy):
Installation with Transformers
For text summarization and Hugging Face models:
Full Installation
For all features:
Development Installation
For contributors who plan to modify the source code:
💡 After installation with "torch", "transformer", or "all" options, it's recommended to run the following CLI command to load the required models:
This is optional but will boost the performance and speed of the crawler. You only need to do this once after installation.
Playwright Installation Note for Ubuntu
If you encounter issues with Playwright installation on Ubuntu, you may need to install additional dependencies:
Option 2: Using Docker (Coming Soon)
Docker support for Crawl4AI is currently in progress and will be available soon. This will allow you to run Crawl4AI in a containerized environment, ensuring consistency across different systems.
Option 3: Local Server Installation
For those who prefer to run Crawl4AI as a local server, instructions will be provided once the Docker implementation is complete.
Verifying Your Installation
After installation, you can verify that Crawl4AI is working correctly by running a simple Python script:
This script should successfully crawl the example website and print the first 500 characters of the extracted content.
Getting Help
If you encounter any issues during installation or usage, please check the documentation or raise an issue on the GitHub repository.
Happy crawling! 🕷️🤖
Page Copy Page Copy
ESC to close
pip install crawl4ai
playwright install # Install Playwright dependencies
pip install crawl4ai[torch]
pip install crawl4ai[transformer]
pip install crawl4ai[all]
git clone https://github.com/unclecode/crawl4ai.git
cd crawl4ai
pip install -e ".[all]"
playwright install # Install Playwright dependencies
crawl4ai-download-models
sudo apt-get install -y \
libwoff1 \
libopus0 \
libwebp7 \
libwebpdemux2 \
libenchant-2-2 \
libgudev-1.0-0 \
libsecret-1-0 \
libhyphen0 \
libgdk-pixbuf2.0-0 \
libegl1 \
libnotify4 \
libxslt1.1 \
libevent-2.1-7 \
libgles2 \
libxcomposite1 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libepoxy0 \
libgtk-3-0 \
libharfbuzz-icu0 \
libgstreamer-gl1.0-0 \
libgstreamer-plugins-bad1.0-0 \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
libxt6 \
libxaw7 \
xvfb \
fonts-noto-color-emoji \
libfontconfig \
libfreetype6 \
xfonts-cyrillic \
xfonts-scalable \
fonts-liberation \
fonts-ipafont-gothic \
fonts-wqy-zenhei \
fonts-tlwg-loma-otf \
fonts-freefont-ttf
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler(verbose=True) as crawler:
result = await crawler.arun(url="https://www.example.com")
print(result.markdown[:500]) # Print first 500 characters
if __name__ == "__main__":
asyncio.run(main())
Browser, Crawler & LLM Configuration (Quick Overview)
Overview of the three core configuration classes in Crawl4AI — BrowserConfig, CrawlerRunConfig, and LLMConfig — explaining their most commonly used parameters, helper methods, and how to combine them…
Overview
Crawl4AI's flexibility stems from two key classes:
BrowserConfig– Dictates how the browser is launched and behaves (e.g., headless or visible, proxy, user agent).
-…
1. BrowserConfig Essentials
Key Fields to Note
1.⠀ browser_type
-
Options:
"chromium","firefox", or"webkit". -
Defaults to
"chromium". -
If you need a different engine, specify it here.
2.⠀…
class BrowserConfig:
def __init__(
browser_type="chromium",
headless=True,
browser_mode="dedicated",
use_managed_browser=False,
cdp_url=None,…
{
"server": "http://proxy.example.com:8080",
"username": "...",
"password": "..."
}
# Create a base browser config
base_browser = BrowserConfig(
browser_type="chromium",
headless=True,
text_mode=True
)
# Create a visible browser config for debugging
debug_browser =…
from crawl4ai import BrowserConfig, CrawlerRunConfig
# At application startup — one time
BrowserConfig.set_defaults(
cache_cdp_connection=True,
cdp_close_delay=0,…
from crawl4ai import AsyncWebCrawler, BrowserConfig
browser_conf = BrowserConfig(
browser_type="firefox",
headless=False,
text_mode=True
)
async with…
2. CrawlerRunConfig Essentials
Key Fields to Note
1.⠀ word_count_threshold :
-
The minimum word count before a block is considered.
-
If your site has lots of short paragraphs or items, you can lower it.
2.⠀…
class CrawlerRunConfig:
def __init__(
word_count_threshold=200,
extraction_strategy=None,
chunking_strategy=RegexChunking(),
markdown_generator=None,…
# Create a base configuration
base_config = CrawlerRunConfig(
cache_mode=CacheMode.ENABLED,
word_count_threshold=200,
wait_until="networkidle"
)
# Create variations for different use…
3. LLMConfig Essentials
Key fields to note
1.⠀ provider :
- Which LLM provider to use.
- Possible values are `"ollama/llama3","groq/llama3-70b-8192","groq/llama3-8b-8192", "openai/gpt-4o-mini"…
llm_config = LLMConfig(
provider="openai/gpt-4o-mini",
api_token=os.getenv("OPENAI_API_KEY"),
backoff_base_delay=1, # optional
backoff_max_attempts=5, # optional…
4. Putting It All Together
In a typical scenario, you define one BrowserConfig for your crawler session, then create one or more CrawlerRunConfig & LLMConfig depending on each call's needs:
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode, LLMConfig, LLMContentFilter, DefaultMarkdownGenerator
from crawl4ai import…
5. Next Steps
For a detailed list of available parameters (including advanced ones), see:
You can explore topics like:
-…
6. Conclusion
BrowserConfig , CrawlerRunConfig and LLMConfig give you straightforward ways to define:
- Which browser to launch, how it should run, and any proxy or user agent needs. -…
Installation & Setup (2023 Edition)
Installation and setup guide for Crawl4AI, covering basic install, diagnostics, verification, optional advanced dependencies, Docker, and local server mode.
1. Basic Installation
This installs the core Crawl4AI library along with essential dependencies. No advanced features (like transformers or PyTorch) are included yet.
pip install crawl4ai
2. Initial Setup & Diagnostics
2.1 Run the Setup Command
After installing, call:
What does it do?
- Installs or updates required browser dependencies for both regular and undetected modes
- Performs OS-level checks (e.g., missing libs on Linux) -…
crawl4ai-setup
2.2 Diagnostics
Optionally, you can run diagnostics to confirm everything is functioning:
This command attempts to:
- Check Python version compatibility
- Verify Playwright installation
- Inspect environment…
crawl4ai-doctor
3. Verifying Installation: A Simple Crawl (Skip this step if you already run `crawl4ai-doctor`)
Below is a minimal Python script demonstrating a basic crawl. It uses our new BrowserConfig and CrawlerRunConfig for clarity, though no custom settings are passed in this…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(…
4. Advanced Installation (Optional)
Warning : Only install these if you truly need them . They bring in larger dependencies, including big models, which can increase disk usage and memory load significantly.
4.1 Torch, Transformers, or All
-
Text Clustering (Torch)
Installs PyTorch-based features (e.g., cosine similarity or advanced semantic chunking).
-
Transformers
Adds Hugging Face-based summarization or generation…
pip install crawl4ai[torch]
crawl4ai-setup
pip install crawl4ai[transformer]
crawl4ai-setup
pip install crawl4ai[all]
crawl4ai-setup
(Optional) Pre-Fetching Models
This step caches large models locally (if needed). Only do this if your workflow requires them.
crawl4ai-download-models
5. Docker (Experimental)
We provide a temporary Docker approach for testing. It’s not stable and may break with future releases. We plan a major Docker revamp in a future stable version, 2025 Q1. If you still…
docker pull unclecode/crawl4ai:basic
docker run -p 11235:11235 unclecode/crawl4ai:basic
6. Local Server Mode (Legacy)
Some older docs mention running Crawl4AI as a local server. This approach has been partially replaced by the new Docker-based prototype and upcoming stable server release. You can experiment,…
Summary
- Install with
pip install crawl4aiand runcrawl4ai-setup. - Diagnose with
crawl4ai-doctorif you see errors. - Verify by crawling
example.comwith minimalBrowserConfig+…
Quick Start - Crawl4AI Documentation (v0.9.x)
Extraction fallback content.
Quick Start - Crawl4AI Documentation (v0.9.x)
Getting Started with Crawl4AI
Welcome to Crawl4AI , an open-source LLM-friendly Web Crawler & Scraper. In this tutorial, you’ll:
-
Run your first crawl using minimal configuration.
-
Generate Markdown output (and learn how it’s influenced by content filters).
-
Experiment with a simple CSS-based extraction strategy.
-
See a glimpse of LLM-based extraction (including open-source and closed-source model options).
-
Crawl a dynamic page that loads content via JavaScript.
1. Introduction
Crawl4AI provides:
-
An asynchronous crawler,
AsyncWebCrawler. -
Configurable browser and run settings via
BrowserConfigandCrawlerRunConfig. -
Automatic HTML-to-Markdown conversion via
DefaultMarkdownGenerator(supports optional filters). -
Multiple extraction strategies (LLM-based or “traditional” CSS/XPath-based).
By the end of this guide, you’ll have performed a basic crawl, generated Markdown, tried out two extraction strategies, and crawled a dynamic page that uses “Load More” buttons or JavaScript updates.
2. Your First Crawl
Here’s a minimal Python script that creates an AsyncWebCrawler , fetches a webpage, and prints the first 300 characters of its Markdown output:
What’s happening?
AsyncWebCrawlerlaunches a headless browser (Chromium by default).- It fetches
https://example.com. - Crawl4AI automatically converts the HTML into Markdown.
You now have a simple, working crawl!
3. Basic Configuration (Light Introduction)
Crawl4AI’s crawler can be heavily customized using two main classes:
1. BrowserConfig : Controls browser behavior (headless or full UI, user agent, JavaScript toggles, etc.).
2. CrawlerRunConfig : Controls how each crawl runs (caching, extraction, timeouts, hooking, etc.).
Below is an example with minimal usage:
IMPORTANT: By default cache mode is set to
CacheMode.BYPASSto have fresh content. SetCacheMode.ENABLEDto enable caching.
We’ll explore more advanced config in later tutorials (like enabling proxies, PDF output, multi-tab sessions, etc.). For now, just note how you pass these objects to manage crawling.
4. Generating Markdown Output
By default, Crawl4AI automatically generates Markdown from each crawled page. However, the exact output depends on whether you specify a markdown generator or content filter .
-
result.markdown:The direct HTML-to-Markdown conversion.
-
result.markdown.fit_markdown:The same content after applying any configured content filter (e.g.,
PruningContentFilter).
Example: Using a Filter with DefaultMarkdownGenerator
Note : If you do not specify a content filter or markdown generator, you’ll typically see only the raw Markdown. PruningContentFilter may adds around 50ms in processing time. We’ll dive deeper into these strategies in a dedicated Markdown Generation tutorial.
5. Simple Data Extraction (CSS-based)
Crawl4AI can also extract structured data (JSON) using CSS or XPath selectors. Below is a minimal CSS-based example:
New! Crawl4AI now provides a powerful utility to automatically generate extraction schemas using LLM. This is a one-time cost that gives you a reusable schema for fast, LLM-free extractions:
For a complete guide on schema generation and advanced usage, see No-LLM Extraction Strategies.
Here's a basic extraction example:
Why is this helpful?
- Great for repetitive page structures (e.g., item listings, articles).
- No AI usage or costs.
- The crawler returns a JSON string you can parse or store.
Tips: You can pass raw HTML to the crawler instead of a URL. To do so, prefix the HTML with
raw://.
6. Simple Data Extraction (LLM-based)
For more complex or irregular pages, a language model can parse text intelligently into a structure you define. Crawl4AI supports open-source or closed-source providers:
-
Open-Source Models (e.g.,
ollama/llama3.3,no_token) -
OpenAI Models (e.g.,
openai/gpt-4, requiresapi_token) -
Or any provider supported by the underlying library
Below is an example using open-source style (no token) and closed-source:
What’s happening?
- We define a Pydantic schema (
PricingInfo) describing the fields we want. - The LLM extraction strategy uses that schema and your instructions to transform raw text into structured JSON.
- Depending on the provider and api_token , you can use local models or a remote API.
7. Adaptive Crawling (New!)
Crawl4AI now includes intelligent adaptive crawling that automatically determines when sufficient information has been gathered. Here's a quick example:
What's special about adaptive crawling?
- Automatic stopping : Stops when sufficient information is gathered
- Intelligent link selection : Follows only relevant links
- Confidence scoring : Know how complete your information is
Learn more about Adaptive Crawling →
8. Multi-URL Concurrency (Preview)
If you need to crawl multiple URLs in parallel , you can use arun_many(). By default, Crawl4AI employs a MemoryAdaptiveDispatcher , automatically adjusting concurrency based on system resources. Here’s a quick glimpse:
The example above shows two ways to handle multiple URLs:
- Streaming mode (
stream=True): Process results as they become available usingasync for - Batch mode (
stream=False): Wait for all results to complete
For more advanced concurrency (e.g., a semaphore-based approach, adaptive memory usage throttling , or customized rate limiting), see Advanced Multi-URL Crawling.
8. Dynamic Content Example
Some sites require multiple “page clicks” or dynamic JavaScript updates. Below is an example showing how to click a “Next Page” button and wait for new commits to load on GitHub, using BrowserConfig and CrawlerRunConfig :
Key Points :
-
BrowserConfig(headless=False): We want to watch it click “Next Page.” -
CrawlerRunConfig(...): We specify the extraction strategy, passsession_idto reuse the same page. -
js_codeandwait_forare used for subsequent pages (page > 0) to click the “Next” button and wait for new commits to load. -
js_only=Trueindicates we’re not re-navigating but continuing the existing session. -
Finally, we call
kill_session()to clean up the page and browser session.
9. Next Steps
Congratulations! You have:
-
Performed a basic crawl and printed Markdown.
-
Used content filters with a markdown generator.
-
Extracted JSON via CSS or LLM strategies.
-
Handled dynamic pages with JavaScript triggers.
If you’re ready for more, check out:
-
Installation : A deeper dive into advanced installs, Docker usage (experimental), or optional dependencies.
-
Hooks & Auth : Learn how to run custom JavaScript or handle logins with cookies, local storage, etc.
-
Deployment : Explore ephemeral testing in Docker or plan for the upcoming stable Docker release.
-
Browser Management : Delve into user simulation, stealth modes, and concurrency best practices.
Crawl4AI is a powerful, flexible tool. Enjoy building out your scrapers, data pipelines, or AI-driven extraction flows. Happy crawling!
Page Copy Page Copy
ESC to close
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun("https://example.com")
print(result.markdown[:300]) # Print first 300 chars
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
async def main():
browser_conf = BrowserConfig(headless=True) # or False to see the browser
run_conf = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS
)
async with AsyncWebCrawler(config=browser_conf) as crawler:
result = await crawler.arun(
url="https://example.com",
config=run_conf
)
print(result.markdown)
if __name__ == "__main__":
asyncio.run(main())
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai.content_filter_strategy import PruningContentFilter
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
md_generator = DefaultMarkdownGenerator(
content_filter=PruningContentFilter(threshold=0.4, threshold_type="fixed")
)
config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
markdown_generator=md_generator
)
async with AsyncWebCrawler() as crawler:
result = await crawler.arun("https://news.ycombinator.com", config=config)
print("Raw Markdown length:", len(result.markdown.raw_markdown))
print("Fit Markdown length:", len(result.markdown.fit_markdown))
from crawl4ai import JsonCssExtractionStrategy
from crawl4ai import LLMConfig
# Generate a schema (one-time cost)
html = "<div class='product'><h2>Gaming Laptop</h2><span class='price'>$999.99</span></div>"
# Using OpenAI (requires API token)
schema = JsonCssExtractionStrategy.generate_schema(
html,
llm_config = LLMConfig(provider="openai/gpt-4o",api_token="your-openai-token") # Required for OpenAI
)
# Or using Ollama (open source, no token needed)
schema = JsonCssExtractionStrategy.generate_schema(
html,
llm_config = LLMConfig(provider="ollama/llama3.3", api_token=None) # Not needed for Ollama
)
# Use the schema for fast, repeated extractions
strategy = JsonCssExtractionStrategy(schema)
import asyncio
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def main():
schema = {
"name": "Example Items",
"baseSelector": "div.item",
"fields": [
{"name": "title", "selector": "h2", "type": "text"},
{"name": "link", "selector": "a", "type": "attribute", "attribute": "href"}
]
}
raw_html = "<div class='item'><h2>Item 1</h2><a href='https://example.com/item1'>Link 1</a></div>"
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="raw://" + raw_html,
config=CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
extraction_strategy=JsonCssExtractionStrategy(schema)
)
)
# The JSON output is stored in 'extracted_content'
data = json.loads(result.extracted_content)
print(data)
if __name__ == "__main__":
asyncio.run(main())
import os
import json
import asyncio
from pydantic import BaseModel, Field
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, LLMConfig
from crawl4ai import LLMExtractionStrategy
class OpenAIModelFee(BaseModel):
model_name: str = Field(..., description="Name of the OpenAI model.")
input_fee: str = Field(..., description="Fee for input token for the OpenAI model.")
output_fee: str = Field(
..., description="Fee for output token for the OpenAI model."
)
async def extract_structured_data_using_llm(
provider: str, api_token: str = None, extra_headers: Dict[str, str] = None
):
print(f"\n--- Extracting Structured Data with {provider} ---")
if api_token is None and provider != "ollama":
print(f"API token is required for {provider}. Skipping this example.")
return
browser_config = BrowserConfig(headless=True)
extra_args = {"temperature": 0, "top_p": 0.9, "max_tokens": 2000}
if extra_headers:
extra_args["extra_headers"] = extra_headers
crawler_config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
word_count_threshold=1,
page_timeout=80000,
extraction_strategy=LLMExtractionStrategy(
llm_config = LLMConfig(provider=provider,api_token=api_token),
schema=OpenAIModelFee.model_json_schema(),
extraction_type="schema",
instruction="""From the crawled content, extract all mentioned model names along with their fees for input and output tokens.
Do not miss any models in the entire content.""",
extra_args=extra_args,
),
)
async with AsyncWebCrawler(config=browser_config) as crawler:
result = await crawler.arun(
url="https://openai.com/api/pricing/", config=crawler_config
)
print(result.extracted_content)
if __name__ == "__main__":
asyncio.run(
extract_structured_data_using_llm(
provider="openai/gpt-4o", api_token=os.getenv("OPENAI_API_KEY")
)
)
import asyncio
from crawl4ai import AsyncWebCrawler, AdaptiveCrawler
async def adaptive_example():
async with AsyncWebCrawler() as crawler:
adaptive = AdaptiveCrawler(crawler)
# Start adaptive crawling
result = await adaptive.digest(
start_url="https://docs.python.org/3/",
query="async context managers"
)
# View results
adaptive.print_stats()
print(f"Crawled {len(result.crawled_urls)} pages")
print(f"Achieved {adaptive.confidence:.0%} confidence")
if __name__ == "__main__":
asyncio.run(adaptive_example())
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def quick_parallel_example():
urls = [
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3"
]
run_conf = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
stream=True # Enable streaming mode
)
async with AsyncWebCrawler() as crawler:
# Stream results as they complete
async for result in await crawler.arun_many(urls, config=run_conf):
if result.success:
print(f"[OK] {result.url}, length: {len(result.markdown.raw_markdown)}")
else:
print(f"[ERROR] {result.url} => {result.error_message}")
# Or get all results at once (default behavior)
run_conf = run_conf.clone(stream=False)
results = await crawler.arun_many(urls, config=run_conf)
for res in results:
if res.success:
print(f"[OK] {res.url}, length: {len(res.markdown.raw_markdown)}")
else:
print(f"[ERROR] {res.url} => {res.error_message}")
if __name__ == "__main__":
asyncio.run(quick_parallel_example())
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def extract_structured_data_using_css_extractor():
print("\n--- Using JsonCssExtractionStrategy for Fast Structured Output ---")
schema = {
"name": "KidoCode Courses",
"baseSelector": "section.charge-methodology .w-tab-content > div",
"fields": [
{
"name": "section_title",
"selector": "h3.heading-50",
"type": "text",
},
{
"name": "section_description",
"selector": ".charge-content",
"type": "text",
},
{
"name": "course_name",
"selector": ".text-block-93",
"type": "text",
},
{
"name": "course_description",
"selector": ".course-content-text",
"type": "text",
},
{
"name": "course_icon",
"selector": ".image-92",
"type": "attribute",
"attribute": "src",
},
],
}
browser_config = BrowserConfig(headless=True, java_script_enabled=True)
js_click_tabs = """
(async () => {
const tabs = document.querySelectorAll("section.charge-methodology .tabs-menu-3 > div");
for(let tab of tabs) {
tab.scrollIntoView();
tab.click();
await new Promise(r => setTimeout(r, 500));
}
})();
"""
crawler_config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
extraction_strategy=JsonCssExtractionStrategy(schema),
js_code=[js_click_tabs],
)
async with AsyncWebCrawler(config=browser_config) as crawler:
result = await crawler.arun(
url="https://www.kidocode.com/degrees/technology", config=crawler_config
)
companies = json.loads(result.extracted_content)
print(f"Successfully extracted {len(companies)} companies")
print(json.dumps(companies[0], indent=2))
async def main():
await extract_structured_data_using_css_extractor()
if __name__ == "__main__":
asyncio.run(main())
Simple Crawling
This guide covers the basics of web crawling with Crawl4AI, including setting up a crawler, making requests, understanding responses, and handling errors.
Basic Usage
Set up a simple crawl using BrowserConfig and CrawlerRunConfig:
import asyncio
from crawl4ai import AsyncWebCrawler
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig
async def main():
browser_config = BrowserConfig() # Default browser…
Understanding the Response
The arun() method returns a CrawlResult object with several useful properties. Here's a quick overview (see CrawlResult for complete details):
config = CrawlerRunConfig(
markdown_generator=DefaultMarkdownGenerator(
content_filter=PruningContentFilter(threshold=0.6),
options={"ignore_links": True}
)
)
result = await…
Adding Basic Options
Customize your crawl using CrawlerRunConfig:
run_config = CrawlerRunConfig(
word_count_threshold=10, # Minimum words per content block
exclude_external_links=True, # Remove external links
remove_overlay_elements=True, #…
Handling Errors
Always check if the crawl was successful:
run_config = CrawlerRunConfig()
result = await crawler.arun(url="https://example.com", config=run_config)
if not result.success:
print(f"Crawl failed: {result.error_message}")
print(f"Status…
Logging and Debugging
Enable verbose logging in BrowserConfig:
browser_config = BrowserConfig(verbose=True)
async with AsyncWebCrawler(config=browser_config) as crawler:
run_config = CrawlerRunConfig()
result = await…
Complete Example
Here's a more comprehensive example demonstrating common usage patterns:
import asyncio
from crawl4ai import AsyncWebCrawler
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig, CacheMode
async def main():
browser_config = BrowserConfig(verbose=True)…
Core Crawling Concepts
Adaptive Web Crawling
Adaptive Web Crawling is a Crawl4AI guide to intelligent crawling, using coverage, consistency, and saturation metrics to decide when enough information has been collected. It covers configuration,…
Introduction
Traditional web crawlers follow predetermined patterns, crawling pages blindly without knowing when they've gathered enough information. Adaptive Crawling changes this paradigm by introducing…
Key Concepts
The Problem It Solves
When crawling websites for specific information, you face two challenges:
- Under-crawling: Stopping too early and missing crucial information
- Over-crawling: Wasting resources by crawling…
How It Works
The AdaptiveCrawler uses three metrics to measure information sufficiency:
- Coverage: How well your collected pages cover the query terms
- Consistency: Whether the information is coherent…
Quick Start
Basic Usage
from crawl4ai import AsyncWebCrawler, AdaptiveCrawler
async def main():
async with AsyncWebCrawler() as crawler:
# Create an adaptive crawler (config is optional)
adaptive =…
Configuration Options
from crawl4ai import AdaptiveConfig
config = AdaptiveConfig(
confidence_threshold=0.8, # Stop when 80% confident (default: 0.7)
max_pages=30, # Maximum pages to crawl…
Crawling Strategies
Adaptive Crawling supports two distinct strategies for determining information sufficiency:
Statistical Strategy (Default)
The statistical strategy uses pure information theory and term-based analysis:
- Fast and efficient - No API calls or model loading
- Term-based coverage - Analyzes query term presence and…
# Default configuration uses statistical strategy
config = AdaptiveConfig(
strategy="statistical", # This is the default
confidence_threshold=0.8
)
Embedding Strategy
The embedding strategy uses semantic embeddings for deeper understanding:
- Semantic understanding - Captures meaning beyond exact term matches
- Query expansion - Automatically generates…
# Configure embedding strategy with local embeddings
config = AdaptiveConfig(
strategy="embedding",
embedding_model="sentence-transformers/all-MiniLM-L6-v2", # Default…
Strategy Comparison
| Feature | Statistical | Embedding |
|---|---|---|
| Speed | Very fast | Moderate (API calls) |
| Cost | Free | Depends on provider |
| Accuracy | Good for exact terms | Excellent… |
Embedding Strategy Configuration
config = AdaptiveConfig(
strategy="embedding",
# Model configuration
embedding_model="sentence-transformers/all-MiniLM-L6-v2",
embedding_llm_config=None, # Use for API-based…
Handling Irrelevant Queries
The embedding strategy can detect when a query is completely unrelated to the content:
# This will stop quickly with low confidence
result = await adaptive.digest(
start_url="https://docs.python.org/3/",
query="how to cook pasta" # Irrelevant to Python docs
)
# Check if query…
When to Use Adaptive Crawling
Perfect For:
- Research Tasks: Finding comprehensive information about a topic
- Question Answering: Gathering sufficient context to answer specific queries
- Knowledge Base Building: Creating focused…
Not Recommended For:
- Full Site Archiving: When you need every page regardless of content
- Structured Data Extraction: When targeting specific, known page patterns
- Real-time Monitoring: When you need…
Understanding the Output
Confidence Score
The confidence score (0-1) indicates how sufficient the gathered information is:
- 0.0-0.3: Insufficient information, needs more crawling
- 0.3-0.6: Partial information, may answer basic…
Statistics Display
The summary shows:
- Pages crawled vs. confidence achieved
- Coverage, consistency, and saturation scores
- Crawling efficiency metrics
adaptive.print_stats(detailed=False) # Summary table
adaptive.print_stats(detailed=True) # Detailed metrics
Persistence and Resumption
Saving Progress
config = AdaptiveConfig(
save_state=True,
state_path="my_crawl_state.json"
)
# Crawl will auto-save progress
result = await adaptive.digest(start_url, query)
Resuming a Crawl
# Resume from saved state
result = await adaptive.digest(
start_url,
query,
resume_from="my_crawl_state.json"
)
Exporting Knowledge Base
# Export collected pages to JSONL
adaptive.export_knowledge_base("knowledge_base.jsonl")
# Import into another session
new_adaptive = AdaptiveCrawler(crawler)
await…
Best Practices
1. Query Formulation
- Use specific, descriptive queries
- Include key terms you expect to find
- Avoid overly broad queries
2. Threshold Tuning
- Start with default (0.7) for general use
- Lower to 0.5-0.6 for exploratory crawling
- Raise to 0.8+ for exhaustive coverage
3. Performance Optimization
- Use appropriate
max_pageslimits - Adjust
top_k_linksbased on site structure - Enable caching for repeat crawls
4. Link Selection
- The crawler prioritizes links based on:
- Relevance to query
- Expected information gain
- URL structure and depth
Examples
Research Assistant
# Gather information about a programming concept
result = await adaptive.digest(
start_url="https://realpython.com",
query="python decorators implementation patterns"
)
# Get the most…
Knowledge Base Builder
# Build a focused knowledge base about machine learning
queries = [
"supervised learning algorithms",
"neural network architectures",
"model evaluation metrics"
]
for query in queries:…
API Documentation Crawler
# Intelligently crawl API documentation
config = AdaptiveConfig(
confidence_threshold=0.85, # Higher threshold for completeness
max_pages=30
)
adaptive = AdaptiveCrawler(crawler,…
Next Steps
- Learn about Advanced Adaptive Strategies
- Explore the AdaptiveCrawler API Reference
- See more…
FAQ
Q: How is this different from traditional crawling? A: Traditional crawling follows fixed patterns (BFS/DFS). Adaptive crawling makes intelligent decisions about which links to follow and when to…
C4A-Script - Crawl4AI Documentation (v0.9.x)
A comprehensive guide to C4A-Script, a human-readable DSL for web automation, covering syntax, commands, examples, and advanced features.
What is C4A-Script?
C4A-Script is a powerful, human-readable domain-specific language (DSL) designed for web automation and interaction. Think of it as a simplified programming language that anyone can read and write,…
# Navigate and interact in plain English
GO https://example.com
WAIT `#search-box` 5
TYPE "Hello World"
CLICK `button[type="submit"]`
Copy
Getting Started: Your First Script
Let's create a simple script that searches for something on a website:
That's it! In just a few lines, you've automated a complete search workflow.
# My first C4A-Script
GO https://duckduckgo.com
# Wait for the search box to appear
WAIT `input[name="q"]` 10
# Type our search query
TYPE "Crawl4AI"
# Press Enter to search
PRESS Enter
# Wait…
Interactive Tutorial & Live Demo
Want to learn by doing? We've got you covered:
🚀 Live Demo - Try C4A-Script in your browser right now!
**📁 [Tutorial…
# Clone and navigate to the tutorial
cd docs/examples/c4a_script/tutorial/
# Install dependencies
pip install -r requirements.txt
# Launch the tutorial server
python server.py
# Open…
Core Concepts
Commands and Syntax
C4A-Script uses simple, English-like commands. Each command does one specific thing:
Selectors: Finding Elements
C4A-Script uses CSS selectors to identify elements on…
# Comments start with #
COMMAND parameter1 parameter2
# Most commands use CSS selectors in backticks
CLICK `#submit-button`
# Text content goes in quotes
TYPE "Hello, World!"
# Numbers are used…
# By ID
CLICK `#login-button`
# By class
CLICK `.submit-btn`
# By attribute
CLICK `button[type="submit"]`
# By accessible attributes
CLICK `button[aria-label="Search"][title="Search"]`
# Complex…
# Set a variable
SETVAR username = "[email protected]"
SETVAR password = "secret123"
# Use variables (prefix with $)
TYPE $username
PRESS Tab
TYPE $password
Copy
Command Categories
🧭 Navigation Commands
Move around the web like a user would:
| Command | Purpose | Example |
|---|---|---|
GO |
Navigate to URL | GO https://example.com |
RELOAD |
Refresh… |
Real-World Examples
Example 1: Login Flow
Example 2: E-commerce Shopping
Example 3: Form Automation with Conditions
# Complete login automation
GO https://myapp.com/login
# Wait for page to load
WAIT `#login-form` 5
# Fill credentials
CLICK `#email`
TYPE "[email protected]"
PRESS Tab
TYPE "mypassword"
# Submit…
# Shopping automation with variables
SETVAR product = "laptop"
SETVAR budget = "1000"
GO https://shop.example.com
WAIT `#search-box` 3
# Search for product
TYPE $product
PRESS Enter
WAIT…
# Smart form filling with error handling
GO https://forms.example.com
# Check if user is already logged in
IF (EXISTS `.user-menu`) THEN GO https://forms.example.com/new
IF (NOT EXISTS `.user-menu`)…
Visual Programming with Blockly
C4A-Script includes a powerful visual programming interface built on Google Blockly. Perfect for:
- Non-programmers who want to create automation
- Rapid prototyping of automation…
Advanced Features
Recording Mode
The tutorial interface includes a recording feature that watches your browser interactions and automatically generates C4A-Script commands:
- Click "Record" in the tutorial…
# Use comments for debugging
# This will wait up to 10 seconds for the element
WAIT `#slow-loading-element` 10
# Check if element exists before clicking
IF (EXISTS `#optional-button`) THEN CLICK…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
# Use C4A-Script for interaction before crawling
script = """
GO https://example.com
CLICK `#load-more-content`
WAIT `.dynamic-content`…
Best Practices
1. Always Wait for Elements
2. Use Descriptive Comments
3. Handle Variable Conditions
4. Use Variables for Reusability
# Bad: Clicking immediately
CLICK `#button`
# Good: Wait for element to appear
WAIT `#button` 5
CLICK `#button`
Copy
# Login to user account
GO https://myapp.com/login
WAIT `#login-form` 5
# Enter credentials
TYPE "[email protected]"
PRESS Tab
TYPE "password123"
# Submit and wait for redirect
CLICK…
# Handle different page states
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept-cookies`
IF (EXISTS `.popup-modal`) THEN CLICK `.close-modal`
# Proceed with main workflow
CLICK `#main-action`
Copy
# Define once, use everywhere
SETVAR base_url = "https://myapp.com"
SETVAR test_email = "[email protected]"
GO $base_url/login
SET `#email` $test_email
Copy
Getting Help
- 📖 Complete Examples - Real-world automation scripts
- 🎮 Interactive Tutorial - Hands-on learning environment
- **📋 [API…
What's Next?
Ready to dive deeper? Check out:
- API Reference - Complete command documentation
- Tutorial Examples - Copy-paste ready scripts -…
Crawl4AI Cache System and Migration Guide
This page explains the new CacheMode enum introduced in Crawl4AI v0.5.0, which replaces old boolean cache flags, and provides migration examples and a mapping table for transitioning from legacy…
Overview
Starting from version 0.5.0, Crawl4AI introduces a new caching system that replaces the old boolean flags with a more intuitive CacheMode enum. This change simplifies cache control and makes the…
Old vs New Approach
The old system used multiple boolean flags:
bypass_cache: Skip cache entirelydisable_cache: Disable all cachingno_cache_read: Don't read from cacheno_cache_write: Don't write to…
Migration Example
Old Code (Deprecated)
New Code (Recommended)
from crawl4ai import AsyncWebCrawler
async def old_code(crawler: AsyncWebCrawler):
# Legacy `bypass_cache` / `disable_cache` / `no_cache_read` / `no_cache_write`
# were removed in v0.5+.…
import asyncio
from crawl4ai import AsyncWebCrawler, CacheMode
from crawl4ai.async_configs import CrawlerRunConfig
async def use_proxy():
# Use CacheMode in CrawlerRunConfig
config =…
Common Migration Patterns
| Legacy Flag | Replacement |
|---|---|
bypass_cache |
cache_mode=CacheMode.BYPASS |
disable_cache |
cache_mode=CacheMode.DISABLED |
no_cache_read |
… |
Deep Crawling - Crawl4AI Documentation (v0.9.x)
This tutorial explains how to perform configurable deep crawling with Crawl4AI, covering BFS, DFS, and BestFirst strategies, streaming vs non-streaming results, filters, scorers, crash recovery,…
Introduction
One of Crawl4AI's most powerful features is its ability to perform configurable deep crawling that can explore websites beyond a single page. With fine-tuned control over crawl depth, domain…
1. Quick Example
Here's a minimal code snippet that implements a basic deep crawl using the BFSDeepCrawlStrategy:
What's happening?
BFSDeepCrawlStrategy(max_depth=2, include_external=False)instructs…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
from crawl4ai.content_scraping_strategy import…
2. Understanding Deep Crawling Strategy Options
2.1 BFSDeepCrawlStrategy (Breadth-First Search)
The BFSDeepCrawlStrategy uses a breadth-first approach, exploring all links at one depth before moving deeper:
Key parameters: -…
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
# Basic configuration
strategy = BFSDeepCrawlStrategy(
max_depth=2, # Crawl initial page + 2 levels deep…
from crawl4ai.deep_crawling import DFSDeepCrawlStrategy
# Basic configuration
strategy = DFSDeepCrawlStrategy(
max_depth=2, # Crawl initial page + 2 levels deep…
from crawl4ai.deep_crawling import BestFirstCrawlingStrategy
from crawl4ai.deep_crawling.scorers import KeywordRelevanceScorer
# Create a scorer
scorer = KeywordRelevanceScorer(…
3. Streaming vs. Non-Streaming Results
Crawl4AI can return results in two modes:
3.1 Non-Streaming Mode (Default)
When to use non-streaming mode:
- You need the complete dataset before processing
- You're performing batch…
config = CrawlerRunConfig(
deep_crawl_strategy=BFSDeepCrawlStrategy(max_depth=1),
stream=False # Default behavior
)
async with AsyncWebCrawler() as crawler:
# Wait for ALL results to be…
config = CrawlerRunConfig(
deep_crawl_strategy=BFSDeepCrawlStrategy(max_depth=1),
stream=True # Enable streaming
)
async with AsyncWebCrawler() as crawler:
# Returns an async iterator…
4. Filtering Content with Filter Chains
Filters help you narrow down which pages to crawl. Combine multiple filters using FilterChain for powerful targeting.
4.1 Basic URL Pattern Filter
4.2 Combining Multiple Filters
###…
from crawl4ai.deep_crawling.filters import FilterChain, URLPatternFilter
# Only follow URLs containing "blog" or "docs"
url_filter = URLPatternFilter(patterns=["*blog*", "*docs*"])
config =…
from crawl4ai.deep_crawling.filters import (
FilterChain,
URLPatternFilter,
DomainFilter,
ContentTypeFilter
)
# Create a chain of filters
filter_chain = FilterChain([
# Only…
5. Using Scorers for Prioritized Crawling
Scorers assign priority values to discovered URLs, helping the crawler focus on the most relevant content first.
5.1 KeywordRelevanceScorer
How scorers work:
- Evaluate each discovered URL…
from crawl4ai.deep_crawling.scorers import KeywordRelevanceScorer
from crawl4ai.deep_crawling import BestFirstCrawlingStrategy
# Create a keyword relevance scorer
keyword_scorer =…
6. Advanced Filtering Techniques
6.1 SEO Filter for Quality Assessment
The SEOFilter helps you identify pages with strong SEO characteristics:
6.2 Content Relevance Filter
The ContentRelevanceFilter analyzes the…
from crawl4ai.deep_crawling.filters import FilterChain, SEOFilter
# Create an SEO filter that looks for specific keywords in page metadata
seo_filter = SEOFilter(
threshold=0.5, # Minimum score…
from crawl4ai.deep_crawling.filters import FilterChain, ContentRelevanceFilter
# Create a content relevance filter
relevance_filter = ContentRelevanceFilter(
query="Web crawling and data…
7. Building a Complete Advanced Crawler
This example combines multiple techniques for a sophisticated crawl:
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_scraping_strategy import LXMLWebScrapingStrategy
from crawl4ai.deep_crawling import…
8. Limiting and Controlling Crawl Size
8.1 Using max_pages
You can limit the total number of pages crawled with the max_pages parameter:
This feature is useful for:
- Controlling API costs
- Setting predictable execution times -…
# Limit to exactly 20 pages regardless of depth
strategy = BFSDeepCrawlStrategy(
max_depth=3,
max_pages=20
)
# Only follow links with scores above 0.4
strategy = DFSDeepCrawlStrategy(
max_depth=2,
url_scorer=KeywordRelevanceScorer(keywords=["api", "guide", "reference"]),
score_threshold=0.4 #…
9. Common Pitfalls & Tips
-
Set realistic limits. Be cautious with
max_depthvalues > 3, which can exponentially increase crawl size. Usemax_pagesto set hard limits. -
Don't neglect the scoring component.…
config = CrawlerRunConfig(
deep_crawl_strategy=BFSDeepCrawlStrategy(max_depth=2),
preserve_https_for_internal_links=True # Keep HTTPS even if server redirects to HTTP
)
10. Crash Recovery for Long-Running Crawls
For production deployments, especially in cloud environments where instances can be terminated unexpectedly, Crawl4AI provides built-in crash recovery support for all deep crawl strategies.
10.1…
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
import json
# Callback to save state after each URL
async def save_state_to_redis(state: dict):
await redis.set("crawl_state",…
{
"strategy_type": "bfs", # or "dfs", "best_first"
"visited": ["url1", "url2", ...], # Already crawled URLs
"pending": [{"url": "...", "parent_url": "..."}], # Queue/stack…
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
# Load saved state (e.g., from Redis, database, or file)
saved_state =…
import json
captured_state = None
async def capture_state(state: dict):
global captured_state
captured_state = state
strategy = BFSDeepCrawlStrategy(
max_depth=2,…
import asyncio
import json
import redis.asyncio as redis
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
REDIS_KEY =…
11. Cancellation Support for Deep Crawls
For production environments like cloud platforms, you often need to stop a running crawl mid-execution—whether the user changed their mind, specified the wrong URL, or wants to control costs.…
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
async def check_if_cancelled():
# Check Redis, database, or any external source
job = await redis.get(f"job:{job_id}")
return…
strategy = BFSDeepCrawlStrategy(max_depth=3, max_pages=1000)
# In another coroutine or thread:
strategy.cancel() # Thread-safe, stops before next URL
async with AsyncWebCrawler() as crawler:
results = await crawler.arun(url, config=config)
if strategy.cancelled:
print(f"Crawl was cancelled after {len(results)} pages")
else:…
async def handle_state(state: dict):
if state.get("cancelled"):
print("Crawl was cancelled!")
print(f"Crawled {state['pages_crawled']} pages before cancellation")
# Save state…
import asyncio
import json
import redis.asyncio as redis
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
async def…
12. Prefetch Mode for Fast URL Discovery
When you need to quickly discover URLs without full page processing, use prefetch mode . This is ideal for two-phase crawling where you first map the site, then selectively process specific…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
config = CrawlerRunConfig(prefetch=True)
async with AsyncWebCrawler() as crawler:
result = await crawler.arun("https://example.com",…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def two_phase_crawl(start_url: str):
async with AsyncWebCrawler() as crawler:
#…
13. Summary & Next Steps
In this Deep Crawling with Crawl4AI tutorial, you learned to:
- Configure BFSDeepCrawlStrategy , DFSDeepCrawlStrategy , and BestFirstCrawlingStrategy
- Process results in streaming…
Domain Mapping: Discover Every URL Under a Domain
This page describes DomainMapper in Crawl4AI, which discovers every URL under a domain using 8 discovery sources, including sitemaps, Common Crawl, Wayback Machine, Certificate Transparency, path…
What Is Domain Mapping?
Domain mapping goes beyond URL seeding. Instead of checking a single sitemap or index, DomainMapper combines 8 discovery sources to find every URL under a domain — including subdomains you…
DomainMapper vs AsyncUrlSeeder
| Aspect | AsyncUrlSeeder | DomainMapper |
|---|---|---|
| Scope | Single host, listed URLs only | Entire domain + all subdomains |
| Sources | Sitemap + Common Crawl | 8 sources… |
Quick Start
Or via AsyncWebCrawler:
import asyncio
from crawl4ai import DomainMapper, DomainMapperConfig
async def main():
async with DomainMapper() as mapper:
results = await mapper.scan("example.com")
print(f"Found…
from crawl4ai import AsyncWebCrawler, DomainMapperConfig
async with AsyncWebCrawler() as crawler:
results = await crawler.amap_domain("example.com")
The 8 Discovery Sources
DomainMapper combines these sources, each catching URLs the others miss:
1. `sitemap` — Sitemap Discovery
Checks /sitemap.xml, /sitemap_index.xml, and robots.txt Sitemap: directives on every discovered host — not just the root domain.
config = DomainMapperConfig(source="sitemap")
2. `cc` — Common Crawl
Queries the Common Crawl CDX API for *.domain.tld/*, catching URLs and subdomains the web's largest public crawl has indexed.
config = DomainMapperConfig(source="cc")
3. `wayback` — Wayback Machine
Queries the Internet Archive's CDX API. Often has different coverage than Common Crawl — including historical pages that have since been removed.
config = DomainMapperConfig(source="wayback")
4. `crt` — Certificate Transparency
Queries crt.sh for SSL certificates issued to *.domain.tld. This is the single most effective subdomain discovery technique — it found 14 subdomains for superdesign.dev that no…
config = DomainMapperConfig(source="crt")
5. `probe` — Common Path Probing
Tries ~25 well-known paths on each discovered host (/docs, /api, /login, /dashboard, /openapi.json, etc.). Combined with soft-404 detection to avoid false positives.
config = DomainMapperConfig(source="probe")
# Add custom paths to probe
config = DomainMapperConfig(
source="probe",
probe_paths=["/custom-api", "/internal/status"]
)
6. `robots` — robots.txt Path Mining
Parses Disallow: and Allow: lines from robots.txt. These are confirmed real paths the site acknowledges exist — often revealing admin panels, APIs, and internal tools that aren't linked…
config = DomainMapperConfig(source="robots")
7. `feed` — RSS/Atom Feed Parsing
Discovers and parses RSS/Atom feeds at common paths (/feed, /rss, /atom.xml, etc.). Feeds are curated lists of content URLs maintained by the site.
config = DomainMapperConfig(source="feed")
8. `homepage` — Homepage Link Extraction
Fetches each host's homepage via HTTP and extracts all internal links using quick_extract_links(). Also mines <link rel="alternate|preload|prefetch"> tags from the `` for additional URLs.…
config = DomainMapperConfig(source="homepage")
Combining Sources
Sources are combined with +:
# Default: most useful combination
config = DomainMapperConfig(source="sitemap+cc+crt+probe")
# Maximum coverage: all 8 sources
config = DomainMapperConfig(…
How It Works: The Three Phases
Phase 1: Host Discovery
DomainMapper first discovers all subdomains under your domain:
Each discovered host is validated with an HTTP HEAD request. Hosts that don't respond are dropped.
superdesign.dev
├── crt.sh → docs, app, cloud, insights, staging-api, ui2web, ...
├── Wayback CDX → api, app, docs, www, ...
├── Common Crawl → app, www, ...
└── DNS guessing →…
Phase 2: Per-Host Scanning
For each validated host, DomainMapper runs all enabled sources in parallel:
docs.superdesign.dev
├── Soft-404 fingerprint → (404 returns proper error — no SPA issue)
├── robots.txt → 1 sitemap URL, 1 disallow path
├── Sitemap parsing → 19 URLs
├── Path…
Phase 3: Post-Processing
All discovered URLs go through:
- URL normalization — using
normalize_url()to canonicalize - Deduplication — by normalized URL, merging source attribution
- Nonsense filtering —…
Soft-404 Detection
Many modern SPAs return HTTP 200 for every URL — even pages that don't exist. DomainMapper detects this:
- Fingerprinting: Fetches a guaranteed-nonexistent URL (e.g.,
/c4ai-probe-a1b2c3d4) on…
# Soft-404 detection is on by default
config = DomainMapperConfig(soft_404_detection=True)
# Disable if you want raw results
config = DomainMapperConfig(soft_404_detection=False)
Configuration Reference
DomainMapperConfig
| Parameter | Type | Default | Description |
|---|---|---|---|
source |
str | "sitemap+cc+crt+probe" |
Discovery sources joined by + |
max_urls |
int | -1 |
Maximum URLs to… |
Output Format
Each result is a dict:
{
"url": "https://docs.superdesign.dev/quickstart",
"host": "docs.superdesign.dev",
"source": "homepage+sitemap", # which source(s) found it
"status": "valid", #…
Practical Examples
Discover and Crawl Documentation
import asyncio
from crawl4ai import AsyncWebCrawler, DomainMapperConfig, CrawlerRunConfig
async def crawl_all_docs():
async with AsyncWebCrawler() as crawler:
# Step 1: Discover all…
Security Audit: Find Exposed Services
async def audit_domain():
async with DomainMapper() as mapper:
results = await mapper.scan("company.com", DomainMapperConfig(
source="crt+probe+robots",…
Compare Subdomains Across a Domain
async def map_infrastructure():
async with DomainMapper() as mapper:
results = await mapper.scan("company.com", DomainMapperConfig(
source="crt+probe",…
Tips and Best Practices
- Start with the default sources (
sitemap+cc+crt+probe). Addwayback,robots,feed, andhomepageif you need maximum coverage. - Use
extract_head=Falsefor speed when you just…
See Also
- URL Seeding — simpler, single-host URL discovery from sitemaps and Common Crawl
- Deep Crawling — follow links dynamically within pages
- [Multi-URL…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| source | str | Discovery sources joined by `+` | sitemap+cc+crt+probe | No |
| max_urls | int | Maximum URLs to return (-1 = unlimited) | -1 | No |
| concurrency | int | Max concurrent requests across all hosts | 50 | No |
| hits_per_sec | int | Rate limit in requests/second | 10 | No |
| force | bool | Bypass all caches | False | No |
| extract_head | bool | Fetch and parse `<head>` metadata | True | No |
| filter_nonsense_urls | bool | Filter static assets and utility URLs | True | No |
| soft_404_detection | bool | Fingerprint and filter soft-404 pages | True | No |
| query | str | BM25 relevance query (requires `extract_head=True`) | None | No |
| score_threshold | float | Minimum relevance score (0.0-1.0) | None | No |
| scoring_method | str | Scoring algorithm | bm25 | No |
| probe_paths | List[str] | Extra paths to probe on each host | None | No |
| common_subdomains | List[str] | Extra subdomain prefixes to guess | None | No |
| use_browser_for_homepage | bool | Use Playwright for JS-rendered homepages | False | No |
| verbose | bool | Override logger verbose setting | None | No |
| cache_ttl_hours | int | Hours before cached results expire | 24 | No |
| dns_timeout | float | Timeout for DNS resolution (seconds) | 3.0 | No |
| http_timeout | float | Timeout for HTTP requests (seconds) | 10.0 | No |
Link & Media - Crawl4AI Documentation (v0.9.x)
This tutorial covers how to extract and filter links (internal/external) and media (images, videos, audio) from crawled pages using Crawl4AI, including advanced link head extraction with scoring,…
1. Link Extraction
When you call arun() or arun_many() on a URL, Crawl4AI automatically extracts links and stores them in the links field of CrawlResult. By default, the crawler tries to distinguish…
from crawl4ai import AsyncWebCrawler
async with AsyncWebCrawler() as crawler:
result = await crawler.arun("https://www.example.com")
if result.success:
internal_links =…
result.links = {
"internal": [
{
"href": "https://kidocode.com/",
"text": "",
"title": "",
"base_domain": "kidocode.com"
},
{
"href":…
2. Advanced Link Head Extraction & Scoring
Ever wanted to not just extract links, but also get the actual content (title, description, metadata) from those linked pages? And score them for relevance? This is exactly what Link Head Extraction…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai import LinkPreviewConfig
async def extract_link_heads_example():
"""
Complete example showing link head…
✅ Successfully crawled: https://docs.python.org/3/
📄 Page title: 3.13.5 Documentation
🔗 Found 53 internal links
🌍 Found 1 external links
🧠 Links with head data extracted: 10
🏆 Top 3 Links with Full…
from crawl4ai import LinkPreviewConfig
link_preview_config = LinkPreviewConfig(
# BASIC SETTINGS
verbose=True, # Show detailed logs (recommended for learning)
# LINK…
# High intrinsic score indicators:
# ✅ Clean URL structure (docs.python.org/api/reference)
# ✅ Meaningful link text ("API Reference Guide")
# ✅ Relevant to page context
# ✅ Not buried deep in…
# Example: query = "machine learning tutorial"
# High contextual score: Link to "Complete Machine Learning Guide"
# Low contextual score: Link to "Privacy Policy"
# When both scores available: (intrinsic * 0.3) + (contextual * 0.7)
# When only intrinsic: uses intrinsic score
# When only contextual: uses contextual score
# When neither: not calculated
async def research_assistant():
config = CrawlerRunConfig(
link_preview_config=LinkPreviewConfig(
include_internal=True,
include_external=True,…
async def api_discovery():
config = CrawlerRunConfig(
link_preview_config=LinkPreviewConfig(
include_internal=True,
include_patterns=["*/api/*", "*/reference/*"],…
async def quality_analysis():
config = CrawlerRunConfig(
link_preview_config=LinkPreviewConfig(
include_internal=True,
max_links=200,
concurrency=20,…
# Check your configuration:
config = CrawlerRunConfig(
link_preview_config=LinkPreviewConfig(
verbose=True # ← Enable to see what's happening
)
)
# Make sure scoring is enabled:
config = CrawlerRunConfig(
score_links=True, # ← Enable intrinsic scoring
link_preview_config=LinkPreviewConfig(
query="your search terms" # ← For…
# Optimize performance:
link_preview_config = LinkPreviewConfig(
max_links=20, # ← Reduce number
concurrency=10, # ← Increase parallelism
timeout=3, # ← Shorter timeout…
3. Domain Filtering
Some websites contain hundreds of third-party or affiliate links. You can filter out certain domains at crawl time by configuring the crawler. The most relevant parameters in CrawlerRunConfig…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
crawler_cfg = CrawlerRunConfig(
exclude_external_links=True, # No links…
crawler_cfg = CrawlerRunConfig(
exclude_domains=["suspiciousads.com"]
)
4. Media Extraction
4.1 Accessing result.media
By default, Crawl4AI collects images, audio and video URLs it finds on the page. These are stored in result.media, a dictionary keyed by media type (e.g.,…
if result.success:
# Get images
images_info = result.media.get("images", [])
print(f"Found {len(images_info)} images in total.")
for i, img in enumerate(images_info[:3]): # Inspect…
result.media = {
"images": [
{
"src": "https://cdn.prod.website-files.com/.../Group%2089.svg",
"alt": "coding school for kids",
"desc": "Trial Class Degrees degrees All…
crawler_cfg = CrawlerRunConfig(
exclude_external_images=True
)
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
crawler_cfg = CrawlerRunConfig(
capture_mhtml=True # Enable MHTML capture
)
async with…
5. Putting It All Together: Link & Media Filtering
Here’s a combined example demonstrating how to filter out external links, skip certain domains, and exclude external images:
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
# Suppose we want to keep only internal links, remove certain domains,
# and discard…
6. Common Pitfalls & Tips
- Conflicting Flags:
exclude_external_links=Truebut then also specifyingexclude_social_media_links=Trueis typically fine, but understand that the first setting already discards all…
Prefix-Based Input Handling in Crawl4AI
This guide demonstrates how to use Crawl4AI to crawl web URLs, local HTML files, and raw HTML strings using prefix-based input handling with the unified `url` parameter and `CrawlerRunConfig`.
Crawling a Web URL
To crawl a live web page, provide the URL starting with http:// or https://, using a CrawlerRunConfig object:
import asyncio
from crawl4ai import AsyncWebCrawler, CacheMode, CrawlerRunConfig
async def crawl_web():
config = CrawlerRunConfig(cache_mode=CacheMode.BYPASS)
async with AsyncWebCrawler() as…
Crawling a Local HTML File
To crawl a local HTML file, prefix the file path with file://.
import asyncio
from crawl4ai import AsyncWebCrawler, CacheMode, CrawlerRunConfig
async def crawl_local_file():
local_file_path = "/path/to/apple.html" # Replace with your file path
file_url…
Crawling Raw HTML Content
To crawl raw HTML content, prefix the HTML string with raw:.
import asyncio
from crawl4ai import AsyncWebCrawler, CacheMode
from crawl4ai.async_configs import CrawlerRunConfig
async def crawl_raw_html():
raw_html = "<html><body><h1>Hello,…
Complete Example
Below is a comprehensive script that:
- Crawls the Wikipedia page for "Apple."
- Saves the HTML content to a local file (
apple.html). - Crawls the local HTML file and verifies the markdown length…
import os
import sys
import asyncio
from pathlib import Path
from crawl4ai import AsyncWebCrawler, CacheMode, CrawlerRunConfig
async def main():
wikipedia_url =…
Conclusion
With the unified url parameter and prefix-based handling in Crawl4AI , you can seamlessly handle web URLs, local HTML files, and raw HTML content. Use CrawlerRunConfig for flexible and…
Page Interaction - Crawl4AI Documentation (v0.9.x)
This page explains how to interact with dynamic webpages using Crawl4AI, covering JavaScript execution, wait conditions, multi-step flows, Shadow DOM flattening, and virtual scrolling.
Page Interaction
Crawl4AI provides powerful features for interacting with dynamic webpages, handling JavaScript execution, waiting for conditions, and managing multi-step flows. By combining js_code,…
1. JavaScript Execution
Basic Execution
js_code in CrawlerRunConfig accepts either a single JS string or a list of JS snippets. It runs after wait_for and delay_before_return_html — so the page is…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
# Single JS command
config = CrawlerRunConfig(
js_code="window.scrollTo(0,…
1. Page navigation (page.goto)
2. js_code_before_wait ← triggers loading / clicks tabs
3. wait_for ← waits for content to appear
4. delay_before_return_html ← extra safety…
config = CrawlerRunConfig(
# Click a tab first
js_code_before_wait="document.querySelector('#specs-tab')?.click();",
# Then wait for the tab content to appear…
2. Wait Conditions
2.1 CSS-Based Waiting
Sometimes, you just want to wait for a specific element to appear. For example:
Key param:
wait_for="css:...": Tells the crawler to wait until that CSS…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
config = CrawlerRunConfig(
# Wait for at least 30 items on Hacker News…
wait_condition = """() => {
const items = document.querySelectorAll('.athing');
return items.length > 50; // Wait for at least 51 items
}"""
config =…
3. Handling Dynamic Content
Many modern sites require multiple steps: scrolling, clicking “Load More,” or updating via JavaScript. Below are typical patterns.
3.1 Load More Example (Hacker News “More” Link)
**Key…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
# Step 1: Load initial Hacker News page
config = CrawlerRunConfig(…
js_form_interaction = """
document.querySelector('#your-search').value = 'TypeScript commits';
document.querySelector('form').submit();
"""
config = CrawlerRunConfig(…
4. Timing Control
page_timeout(ms): Overall page load or script execution time limit.delay_before_return_html(seconds): Wait an extra moment before capturing the final HTML.mean_delay&…
config = CrawlerRunConfig(
page_timeout=60000, # 60s limit
delay_before_return_html=2.5
)
5. Multi-Step Interaction Example
Below is a simplified script that does multiple “Load More” clicks on GitHub’s TypeScript commits page. It re-uses the same session to accumulate new commits each time. The code includes the…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
async def multi_page_commits():
browser_cfg = BrowserConfig(
headless=False, # Visible…
6. Combine Interaction with Extraction
Once dynamic content is loaded, you can attach an extraction_strategy (like JsonCssExtractionStrategy or LLMExtractionStrategy). For example:
When done, check result.extracted_content…
from crawl4ai import JsonCssExtractionStrategy
schema = {
"name": "Commits",
"baseSelector": "li.Box-sc-g0xbh4-0",
"fields": [
{"name": "title", "selector": "h4.markdown-title",…
7. Shadow DOM Flattening
Sites built with Web Components (Stencil, Lit, Shoelace, etc.) render content inside Shadow DOM — an encapsulated sub-tree that is invisible to normal page serialization. Set…
config = CrawlerRunConfig(
flatten_shadow_dom=True,
wait_until="load",
delay_before_return_html=3.0, # give components time to hydrate
)
8. Relevant `CrawlerRunConfig` Parameters
Below are the key interaction-related parameters in CrawlerRunConfig. For a full list, see Configuration Parameters.
js_code: JavaScript to run afterwait_for+…
9. Conclusion
Crawl4AI's page interaction features let you:
- Execute JavaScript for scrolling, clicks, or form filling.
- Wait for CSS or custom JS conditions before capturing data.
- Handle…
10. Virtual Scrolling
For sites that use virtual scrolling (where content is replaced rather than appended as you scroll, like Twitter or Instagram), Crawl4AI provides a dedicated VirtualScrollConfig:
Virtual…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, VirtualScrollConfig
async def crawl_twitter_timeline():
# Configure virtual scroll for Twitter-like feeds
virtual_config =…
URL Seeding: The Smart Way to Crawl at Scale
This page explains how to use URL seeding to discover and filter URLs before crawling, covering configuration, smart filtering with BM25 scoring, and scaling across multiple domains.
Why URL Seeding?
Web crawling comes in different flavors, each with its own strengths. Let's understand when to use URL seeding versus deep crawling.
Deep Crawling: Real-Time Discovery
Deep crawling is perfect when you need:
- Fresh, real-time data - discovering pages as they're created
- Dynamic exploration - following links based on content
- Selective extraction -…
# Deep crawling example: Explore a website dynamically
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy
async def…
URL Seeding: Bulk Discovery
URL seeding shines when you want:
- Comprehensive coverage - get thousands of URLs in seconds
- Bulk processing - filter before crawling
- Resource efficiency - know exactly what you'll…
# URL seeding example: Analyze all documentation
from crawl4ai import AsyncUrlSeeder, SeedingConfig
seeder = AsyncUrlSeeder()
config = SeedingConfig(
source="sitemap",
extract_head=True,…
The Trade-offs
| Aspect | Deep Crawling | URL Seeding |
|---|---|---|
| Coverage | Discovers pages dynamically | Gets most existing URLs instantly |
| Freshness | Finds brand new pages | May miss very… |
When to Use Each
Choose Deep Crawling when:
- You need the absolute latest content
- You're searching for specific information
- The site structure is unknown or dynamic
- You want to stop as soon as you find…
Your First URL Seeding Adventure
Let's see the magic in action. We'll discover blog posts about Python, filter for tutorials, and crawl only those pages.
What just happened?
- We discovered all blog URLs from the sitemap+cc -…
import asyncio
from crawl4ai import AsyncUrlSeeder, AsyncWebCrawler, SeedingConfig, CrawlerRunConfig
async def smart_blog_crawler():
# Step 1: Create our URL discoverer
seeder =…
Understanding the URL Seeder
Now that you've seen the magic, let's understand how it works.
Basic Usage
Creating a URL seeder is simple:
The seeder can discover URLs from two powerful sources:
from crawl4ai import AsyncUrlSeeder
# Method 1: Manual cleanup
seeder = AsyncUrlSeeder()
try:
config = SeedingConfig(source="sitemap")
urls = await seeder.urls("example.com",…
1. Sitemaps (Fastest)
Sitemaps are XML files that websites create specifically to list all their URLs. It's like getting a menu at a restaurant - everything is listed upfront.
Sitemap Index Support: For large…
# Discover from sitemap
config = SeedingConfig(source="sitemap")
urls = await seeder.urls("example.com", config)
<!-- Example sitemap index -->
<sitemapindex>
<sitemap>
<loc>https://techcrunch.com/sitemap-1.xml</loc>
</sitemap>
<sitemap>
<loc>https://techcrunch.com/sitemap-2.xml</loc>…
2. Common Crawl (Most Comprehensive)
Common Crawl is a massive public dataset that regularly crawls the entire web. It's like having access to a pre-built index of the internet.
# Discover from Common Crawl
config = SeedingConfig(source="cc")
urls = await seeder.urls("example.com", config)
3. Both Sources (Maximum Coverage)
# Use both sources
config = SeedingConfig(source="sitemap+cc")
urls = await seeder.urls("example.com", config)
Configuration Magic: SeedingConfig
The SeedingConfig object is your control panel. Here's everything you can configure:
Pattern Matching Examples
# Match all blog posts
config = SeedingConfig(pattern="*/blog/*")
# Match only HTML files
config = SeedingConfig(pattern="*.html")
# Match product pages
config =…
URL Validation: Live Checking
Sometimes you need to know if URLs are actually accessible. That's where live checking comes in:
When to use live checking:
- Before a large crawling operation
- When working with older…
config = SeedingConfig(
source="sitemap",
live_check=True, # Verify each URL is accessible
concurrency=20 # Check 20 URLs in parallel
)
async with AsyncUrlSeeder() as seeder:
urls…
The Power of Metadata: Head Extraction
This is where URL seeding gets really powerful. Instead of crawling entire pages, you can extract just the metadata:
config = SeedingConfig(
extract_head=True # Extract metadata from <head> section
)
async with AsyncUrlSeeder() as seeder:
urls = await seeder.urls("example.com", config)
# Now each URL has…
What Can We Extract?
The head extraction gives you a treasure trove of information:
# Example of extracted head_data
{
"title": "10 Python Tips for Beginners",
"charset": "utf-8",
"lang": "en",
"meta": {
"description": "Learn essential Python tips...",…
Smart URL-Based Filtering (No Head Extraction)
When extract_head=False but you still provide a query, the seeder uses intelligent URL-based scoring:
This approach is much faster than head extraction while still providing intelligent filtering!
# Fast filtering based on URL structure alone
config = SeedingConfig(
source="sitemap",
extract_head=False, # Don't fetch page metadata
query="python tutorial async",…
Understanding Results
Each URL in the results has this structure:
Let's see a real example:
{
"url": "https://example.com/blog/python-tips.html",
"status": "valid", # "valid", "not_valid", or "unknown"
"head_data": { # Only if extract_head=True
"title":…
config = SeedingConfig(
source="sitemap",
extract_head=True,
live_check=True
)
async with AsyncUrlSeeder() as seeder:
urls = await seeder.urls("blog.example.com", config)
# Analyze…
Smart Filtering with BM25 Scoring
Now for the really cool part - intelligent filtering based on relevance!
Introduction to Relevance Scoring
BM25 is a ranking algorithm that scores how relevant a document is to a search query. With URL seeding, we can score URLs based on their metadata before crawling them.
Think of it like this: -…
Query-Based Discovery
Here's how to use BM25 scoring:
config = SeedingConfig(
source="sitemap",
extract_head=True, # Required for scoring
query="python async tutorial", # What we're looking for
scoring_method="bm25", #…
Real Examples
Finding Documentation Pages
# Find API documentation
config = SeedingConfig(
source="sitemap",
extract_head=True,
query="API reference documentation endpoints",
scoring_method="bm25",
score_threshold=0.5,…
Discovering Product Pages
# Find specific products
config = SeedingConfig(
source="sitemap+cc", # Use both sources
extract_head=True,
query="wireless headphones noise canceling",
scoring_method="bm25",…
Filtering News Articles
# Find recent news about AI
config = SeedingConfig(
source="sitemap",
extract_head=True,
query="artificial intelligence machine learning breakthrough",
scoring_method="bm25",…
Complex Query Patterns
# Multi-concept queries
queries = [
"python async await concurrency tutorial",
"data science pandas numpy visualization",
"web scraping beautifulsoup selenium automation",
"machine…
Scaling Up: Multiple Domains
When you need to discover URLs across multiple websites, URL seeding really shines.
The `many_urls` Method
# Discover URLs from multiple domains in parallel
domains = ["site1.com", "site2.com", "site3.com"]
config = SeedingConfig(
source="sitemap",
extract_head=True,
query="python tutorial",…
Cross-Domain Examples
Competitor Analysis
# Analyze content strategies across competitors
competitors = [
"competitor1.com",
"competitor2.com",
"competitor3.com"
]
config = SeedingConfig(
source="sitemap",…
Industry Research
# Research Python tutorials across educational sites
educational_sites = [
"realpython.com",
"pythontutorial.net",
"learnpython.org",
"python.org"
]
config = SeedingConfig(…
Multi-Site Monitoring
# Monitor news about your company across multiple sources
news_sites = [
"techcrunch.com",
"theverge.com",
"wired.com",
"arstechnica.com"
]
company_name = "YourCompany"
config =…
Advanced Integration Patterns
Let's put everything together in a real-world example.
Building a Research Assistant
Here's a complete example that discovers, scores, filters, and crawls intelligently:
import asyncio
from datetime import datetime
from crawl4ai import AsyncUrlSeeder, AsyncWebCrawler, SeedingConfig, CrawlerRunConfig
class ResearchAssistant:
def __init__(self):…
Performance Optimization Tips
# First run - populate cache
config = SeedingConfig(source="sitemap", extract_head=True, force=True)
urls = await seeder.urls("example.com", config)
# Subsequent runs - use cache (much…
# For many small requests (like HEAD checks)
config = SeedingConfig(concurrency=50, hits_per_sec=20)
# For fewer large requests (like full head extraction)
config = SeedingConfig(concurrency=10,…
# When crawling many URLs
async with AsyncWebCrawler() as crawler:
# Assuming urls is a list of URL strings
crawl_results = await crawler.arun_many(urls, config=config)
# Process as they…
# Safe for domains with 1M+ URLs
config = SeedingConfig(
source="cc+sitemap",
concurrency=50, # Queue size adapts to concurrency
max_urls=100000 # Process in batches if needed
)
# The…
Best Practices & Tips
Cache Management
The seeder automatically caches results to speed up repeated operations:
- **Common Crawl cache** : `~/.crawl4ai/seeder_cache/[index]_[domain]_[hash].jsonl`
- **Sitemap cache** : `~/.crawl4ai/seeder_cache/sitemap_[domain]_[hash].json`
- **HEAD data cache** :…
Smart TTL Cache for Sitemaps
Sitemap caches now include intelligent validation:
Cache validation priority:
force=True→ Always refetch- Cache doesn't exist → Fetch fresh
validate_sitemap_lastmod=Trueand…
# Default: 24-hour TTL with lastmod validation
config = SeedingConfig(
source="sitemap",
cache_ttl_hours=24, # Cache expires after 24 hours
validate_sitemap_lastmod=True #…
Pattern Matching Strategies
# Be specific when possible
good_pattern = "*/blog/2024/*.html" # Specific
bad_pattern = "*" # Too broad
# Combine patterns with metadata filtering
config = SeedingConfig(…
Rate Limiting Considerations
# Be respectful of servers
config = SeedingConfig(
hits_per_sec=10, # Max 10 requests per second
concurrency=20 # But use 20 workers
)
# For your own servers
config =…
Quick Reference
Common Patterns
# Blog post discovery
config = SeedingConfig(
source="sitemap",
pattern="*/blog/*",
extract_head=True,
query="your topic",
scoring_method="bm25"
)
# E-commerce product…
Troubleshooting Guide
| Issue | Solution |
|---|---|
| No URLs found | Try source="cc+sitemap", check domain spelling |
| Slow discovery | Reduce concurrency, add hits_per_sec limit |
| Missing metadata | Ensure… |
Performance Benchmarks
Typical performance on a standard connection:
- Sitemap discovery : 100-1,000 URLs/second
- Common Crawl discovery : 50-500 URLs/second
- HEAD checking : 10-50 URLs/second
- **Head…
Conclusion
URL seeding transforms web crawling from a blind expedition into a surgical strike. By discovering and analyzing URLs before crawling, you can:
- Save hours of crawling time
- Reduce bandwidth usage…
Smart URL Filtering
The seeder automatically filters out nonsense URLs that aren't useful for content crawling:
To disable filtering (not recommended):
# Enabled by default
config = SeedingConfig(
source="sitemap",
filter_nonsense_urls=True # Default: True
)
# URLs that get filtered:
# - robots.txt, sitemap.xml, ads.txt
# - API endpoints…
config = SeedingConfig(
source="sitemap",
filter_nonsense_urls=False # Include ALL URLs
)
Key Features Summary
- Parallel Sitemap Index Processing : Automatically detects and processes sitemap indexes in parallel
- Memory Protection : Bounded queues prevent RAM issues with large domains (1M+ URLs) -…
Need More Coverage?
If you need to discover URLs across an entire domain — including subdomains, hidden services, and pages not listed in any sitemap — check out Domain Mapping. It combines 8…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| source | str | URL source: "cc" (Common Crawl), "sitemap", or "sitemap+cc" | "sitemap+cc" | No |
| pattern | str | URL pattern filter (e.g., "*/blog/*", "*.html") | "*" | No |
| extract_head | bool | Extract metadata from page <head> | False | No |
| live_check | bool | Verify URLs are accessible | False | No |
| max_urls | int | Maximum URLs to return (-1 = unlimited) | -1 | No |
| concurrency | int | Parallel workers for fetching | 10 | No |
| hits_per_sec | int | Rate limit for requests | 5 | No |
| force | bool | Bypass cache, fetch fresh data | False | No |
| verbose | bool | Show detailed progress | False | No |
| query | str | Search query for BM25 scoring | None | No |
| scoring_method | str | Scoring method (currently "bm25") | None | No |
| score_threshold | float | Minimum score to include URL | None | No |
| filter_nonsense_urls | bool | Filter out utility URLs (robots.txt, etc.) | True | No |
| cache_ttl_hours | int | Hours before sitemap cache expires (0 = no TTL) | 24 | No |
| validate_sitemap_lastmod | bool | Check sitemap's lastmod and refetch if newer | True | No |
Content & Output
Content Selection
This page explains how to select, filter, and refine content from crawls using CrawlerRunConfig parameters, including CSS selectors, content filtering, iframe handling, shadow DOM flattening, and…
1. CSS-Based Selection
Crawl4AI provides multiple ways to select, filter, and refine the content from your crawls. Whether you need to target a specific CSS region, exclude entire tags, filter out external links, or remove…
1.1 Using css_selector
A straightforward way to limit your crawl results to a certain region of the page is css_selector in CrawlerRunConfig:
Result: Only elements matching that selector remain in result.cleaned_html.
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
config = CrawlerRunConfig(
# e.g., first 30 items from Hacker News…
1.2 Using target_elements
The target_elements parameter provides more flexibility by allowing you to target multiple elements for content extraction while preserving the entire page context for other features:
Key…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
config = CrawlerRunConfig(
# Target article body and sidebar, but not other content…
2. Content Filtering & Exclusions
2.1 Basic Overview
Explanation:
- word_count_threshold: Ignores text blocks under X words. Helps skip trivial blocks like short nav or disclaimers.
- excluded_tags: Removes entire tags (
config = CrawlerRunConfig(
# Content thresholds
word_count_threshold=10, # Minimum words per block
# Tag exclusions
excluded_tags=['form', 'header', 'footer', 'nav'],
#…
[
'facebook.com',
'twitter.com',
'x.com',
'linkedin.com',
'instagram.com',
'pinterest.com',
'tiktok.com',
'snapchat.com',
'reddit.com',
]
2.2 Example Usage
Note: If these parameters remove too much, reduce or disable them accordingly.
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
config = CrawlerRunConfig(
css_selector="main.content",…
3. Handling Iframes
Some sites embed content in
Usage:
config = CrawlerRunConfig(
# Merge iframe content into the final output
process_iframes=True,
remove_overlay_elements=True,
# Remove GDPR/cookie consent popups (OneTrust, Cookiebot,…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
config = CrawlerRunConfig(
process_iframes=True,
remove_overlay_elements=True
)…
3.1 Flattening Shadow DOM
Sites built with Web Components (Stencil, Lit, Shoelace, Angular Elements, etc.) render content inside Shadow DOM — an encapsulated sub-tree that is invisible to normal page serialization. The…
config = CrawlerRunConfig(
# Flatten shadow DOM into the main document
flatten_shadow_dom=True,
# Give web components time to hydrate
wait_until="load",…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
config = CrawlerRunConfig(
flatten_shadow_dom=True,
wait_until="load",…
4. Structured Extraction Examples
You can combine content selection with a more advanced extraction strategy. For instance, a CSS-based or LLM-based extraction strategy can run on the filtered HTML.
4.1 Pattern-Based with JsonCssExtractionStrategy
import asyncio
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def main():
# Minimal schema for repeated items…
4.2 LLM-Based Extraction
Here, the crawler:
- Filters out external links (exclude_external_links=True).
- Ignores very short text blocks (word_count_threshold=20).
- Passes the final HTML to your LLM strategy for an…
import asyncio
import json
from pydantic import BaseModel, Field
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, LLMConfig
from crawl4ai import LLMExtractionStrategy
class…
5. Comprehensive Example
Below is a short function that unifies CSS selection, exclusion logic, and a pattern-based extraction, demonstrating how you can fine-tune your final data:
Why This Works:
- CSS scoping with…
import asyncio
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def extract_main_articles(url: str):
schema = {…
6. Scraping Modes
Crawl4AI uses LXMLWebScrapingStrategy (LXML-based) as the default scraping strategy for HTML content processing. This strategy offers excellent performance, especially for large HTML…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, LXMLWebScrapingStrategy
async def main():
# Default configuration already uses LXMLWebScrapingStrategy
config = CrawlerRunConfig()…
from crawl4ai import ContentScrapingStrategy, ScrapingResult, MediaItem, Media, Link, Links
class CustomScrapingStrategy(ContentScrapingStrategy):
def scrap(self, url: str, html: str, **kwargs)…
7. Combining CSS Selection Methods
You can combine css_selector and target_elements in powerful ways to achieve fine-grained control over your output:
This approach gives you the best of both worlds:
- Markdown generation and content…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
# Target specific content but preserve page context
config = CrawlerRunConfig(
#…
8. Conclusion
By mixing target_elements or css_selector scoping, content filtering parameters, and advanced extraction strategies, you can precisely choose which data to keep. Key parameters in CrawlerRunConfig…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| css_selector | string | A CSS selector to limit crawl results to a certain region of the page. Only elements matching that selector remain in result.cleaned_html. | No | |
| target_elements | array of strings | Array of CSS selectors to focus markdown generation and data extraction on those elements, while preserving full page context for links and media. | No | |
| word_count_threshold | integer | Minimum words per block. Ignores text blocks under X words. | No | |
| excluded_tags | array of strings | List of HTML tags to remove from the content. | No | |
| exclude_external_links | boolean | If True, strips out external links and may remove them from result.links. | No | |
| exclude_social_media_links | boolean | If True, removes links pointing to known social media domains. | No | |
| exclude_domains | array of strings | A custom list of domains to block if discovered in links. | No | |
| exclude_social_media_domains | array of strings | A curated list of social media domains to exclude. Override or add to it. | No | |
| exclude_external_images | boolean | If True, discards images not hosted on the same domain as the main page (or its subdomains). | No | |
| process_iframes | boolean | If True, merges iframe content into the final output. | No | |
| remove_overlay_elements | boolean | If True, removes overlay elements. | No | |
| remove_consent_popups | boolean | If True, removes GDPR/cookie consent popups (OneTrust, Cookiebot, etc.). | No | |
| flatten_shadow_dom | boolean | If True, flattens shadow DOM into the main document. | No | |
| wait_until | string | Wait condition for page load (e.g., 'load'). | No | |
| delay_before_return_html | float | Delay in seconds before returning HTML. | No | |
| cache_mode | CacheMode | Caching behavior (e.g., CacheMode.BYPASS). | No | |
| extraction_strategy | ExtractionStrategy | Strategy for structured extraction (e.g., JsonCssExtractionStrategy, LLMExtractionStrategy). | No | |
| scraping_strategy | ScrapingStrategy | Strategy for HTML processing (e.g., LXMLWebScrapingStrategy). | No |
Crawl Result and Output
Describes the CrawlResult object returned by Crawl4AI's arun() method, including all fields, markdown generation, structured extraction, and additional outputs like links, media, tables, screenshots,…
1. The `CrawlResult` Model
Below is the core schema. Each field captures a different aspect of the crawl’s result:
class MarkdownGenerationResult(BaseModel):
raw_markdown: str
markdown_with_citations: str…
class MarkdownGenerationResult(BaseModel):
raw_markdown: str
markdown_with_citations: str
references_markdown: str
fit_markdown: Optional[str] = None
fit_html: Optional[str] =…
2. HTML Variants
html: Raw HTML
Crawl4AI preserves the exact HTML as result.html. Useful for:
- Debugging page issues or checking the original content.
- Performing your own specialized parse if…
config = CrawlerRunConfig(
excluded_tags=["form", "header", "footer"],
keep_data_attributes=False
)
result = await crawler.arun("https://example.com",…
3. Markdown Generation
3.1 markdown
markdown: The current location for detailed markdown output, returning aMarkdownGenerationResultobject.markdown_v2: Removed in v0.5. Accessing it now…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
config = CrawlerRunConfig(…
4. Structured Extraction: `extracted_content`
If you run a JSON-based extraction strategy (CSS, XPath, LLM, etc.), the structured data is not stored in markdown—it’s placed in result.extracted_content as a JSON string (or sometimes…
import asyncio
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def main():
schema = {
"name": "Example…
5. More Fields: Links, Media, Tables and More
5.1 links
A dictionary, typically with "internal" and "external" lists. Each entry might have href, text, title, etc. This is automatically captured if you haven’t disabled link…
print(result.links["internal"][:3]) # Show first 3 internal links
images = result.media.get("images", [])
for img in images:
print("Image URL:", img["src"], "Alt:", img.get("alt"))
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(…
config = CrawlerRunConfig(
table_score_threshold=5 # Lower value = more tables detected (default: 7)
)
# Save the PDF
with open("page.pdf", "wb") as f:
f.write(result.pdf)
# Save the MHTML
if result.mhtml:
with open("page.mhtml", "w", encoding="utf-8") as f:
f.write(result.mhtml)
6. Accessing These Fields
After you run:
result = await crawler.arun(url="https://example.com", config=some_config)
Check any field:
if result.success:
print(result.status_code, result.response_headers)…
result = await crawler.arun(url="https://example.com", config=some_config)
if result.success:
print(result.status_code, result.response_headers)
print("Links found:", len(result.links.get("internal", [])))
if result.markdown:
print("Markdown snippet:",…
7. Next Steps
- Markdown Generation: Dive deeper into how to configure
DefaultMarkdownGeneratorand various filters. - Content Filtering: Learn how to use
BM25ContentFilterand…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| url | str | The final or actual URL crawled (in case of redirects). | Yes | |
| html | str | Original, unmodified page HTML. Good for debugging or custom processing. | Yes | |
| fit_html | Optional[str] | Preprocessed HTML optimized for extraction and content filtering. | No | |
| success | bool | True if the crawl completed without major errors, else False. | Yes | |
| cleaned_html | Optional[str] | Sanitized HTML with scripts/styles removed; can exclude tags if configured via excluded_tags etc. | No | |
| media | Dict[str, List[Dict]] | Extracted media info (images, audio, etc.), each with attributes like src, alt, score, etc. | {} | No |
| links | Dict[str, List[Dict]] | Extracted link data, split by internal and external. Each link usually has href, text, etc. | {} | No |
| downloaded_files | Optional[List[str]] | If accept_downloads=True in BrowserConfig, this lists the filepaths of saved downloads. | No | |
| js_execution_result | Optional[Dict[str, Any]] | Results from JavaScript execution during crawling. | No | |
| screenshot | Optional[str] | Screenshot of the page (base64-encoded) if screenshot=True. | No | |
| Optional[bytes] | PDF of the page if pdf=True. | No | ||
| mhtml | Optional[str] | MHTML snapshot of the page if capture_mhtml=True. Contains the full page with all resources. | No | |
| markdown | Optional[Union[str, MarkdownGenerationResult]] | It holds a MarkdownGenerationResult. Over time, this will be consolidated into markdown. The generator can provide raw markdown, citations, references, and optionally fit_markdown. | No | |
| extracted_content | Optional[str] | The output of a structured extraction (CSS/LLM-based) stored as JSON string or other text. | No | |
| metadata | Optional[dict] | Additional info about the crawl or extracted data. | No | |
| error_message | Optional[str] | If success=False, contains a short description of what went wrong. | No | |
| session_id | Optional[str] | The ID of the session used for multi-page or persistent crawling. | No | |
| response_headers | Optional[dict] | HTTP response headers, if captured. | No | |
| status_code | Optional[int] | HTTP status code (e.g., 200 for OK). | No | |
| ssl_certificate | Optional[SSLCertificate] | SSL certificate info if fetch_ssl_certificate=True. | No | |
| dispatch_result | Optional[DispatchResult] | Additional concurrency and resource usage information when crawling URLs in parallel. | No | |
| redirected_url | Optional[str] | The URL after any redirects (different from url which is the final URL). | No | |
| redirected_status_code | Optional[int] | HTTP status code of the final redirect destination (e.g., 200). None for non-HTTP requests (raw HTML, local files). | No | |
| network_requests | Optional[List[Dict[str, Any]]] | List of network requests, responses, and failures captured during the crawl if capture_network_requests=True. | No | |
| console_messages | Optional[List[Dict[str, Any]]] | List of browser console messages captured during the crawl if capture_console_messages=True. | No | |
| tables | List[Dict] | Table data extracted from HTML tables with structure [{headers, rows, caption, summary}]. | [] | No |
Llmtxt - Crawl4AI Documentation (v0.9.x)
This page documents the Llmtxt feature in Crawl4AI, which provides LLM-friendly text for crawled content. The provided raw content contains only navigation and UI elements, so the actual…
Llmtxt
The provided raw content for this page contains only navigation and UI elements. No actual documentation text was found in the provided content.
Markdown Generation Basics
This tutorial explains how to generate clean, structured markdown from web pages using Crawl4AI's DefaultMarkdownGenerator, including configuration options, content filters (BM25, Pruning, LLM), and…
Prerequisites
Prerequisites
You’ve completed or read AsyncWebCrawler Basics to understand how to run a simple crawl.
You know how to configure
CrawlerRunConfig.
1. Quick Example
Here’s a minimal code snippet that uses the DefaultMarkdownGenerator with no additional filtering:
What’s happening?
CrawlerRunConfig( markdown_generator = DefaultMarkdownGenerator() )…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
async def main():
config = CrawlerRunConfig(…
2. How Markdown Generation Works
Under the hood, DefaultMarkdownGenerator uses a specialized HTML-to-text approach that:
- Preserves headings, code blocks, bullet points, etc.
- Removes extraneous tags (scripts, styles) that…
2.1 HTML-to-Text Conversion (Forked & Modified)
Under the hood, DefaultMarkdownGenerator uses a specialized HTML-to-text approach that:
- Preserves headings, code blocks, bullet points, etc.
- Removes extraneous tags (scripts, styles) that…
2.2 Link Citations & References
By default, the generator can convert <a href="..."> elements into [text][1] citations, then place the actual links at the bottom of the document. This is handy for research workflows that demand…
2.3 Optional Content Filters
Before or after the HTML-to-Markdown step, you can apply a content filter (like BM25 or Pruning) to reduce noise and produce a “fit_markdown”—a heavily pruned version focusing on the page’s main…
3. Configuring the Default Markdown Generator
You can tweak the output by passing an options dict to DefaultMarkdownGenerator. For example:
Some commonly used options:
ignore_links(bool): Whether to remove all hyperlinks in the…
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
# Example: ignore all links, don't escape…
4. Selecting the HTML Source for Markdown Generation
The content_source parameter allows you to control which HTML content is used as input for markdown generation. This gives you flexibility in how the HTML is processed before conversion to markdown.
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
# Option 1: Use the raw HTML directly from the…
HTML Source Options
"cleaned_html"(default): Uses the HTML after it has been processed by the scraping strategy. This HTML is typically cleaner and more focused on content, with some boilerplate removed. -…
When to Use Each Option
- Use
"cleaned_html"(default) for most cases where you want a balance of content preservation and noise removal. - Use
"raw_html"when you need to preserve all original content, or when…
5. Content Filters
Content filters selectively remove or rank sections of text before turning them into Markdown. This is especially helpful if your page has ads, nav bars, or other clutter you don’t want.
5.1 BM25ContentFilter
If you have a search query, BM25 is a good choice:
user_query: The term you want to focus on. BM25 tries to keep only content blocks relevant to that query.bm25_threshold: Raise…
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
from crawl4ai.content_filter_strategy import BM25ContentFilter
from crawl4ai import CrawlerRunConfig
bm25_filter =…
5.2 PruningContentFilter
If you don’t have a specific query, or if you just want a robust “junk remover,” use PruningContentFilter. It analyzes text density, link density, HTML structure, and known patterns (like…
from crawl4ai.content_filter_strategy import PruningContentFilter
prune_filter = PruningContentFilter(
threshold=0.5,
threshold_type="fixed", # or "dynamic"
min_word_threshold=50
)
5.3 LLMContentFilter
For intelligent content filtering and high-quality markdown generation, you can use the LLMContentFilter. This filter leverages LLMs to generate relevant markdown while preserving the original…
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, LLMConfig, DefaultMarkdownGenerator
from crawl4ai.content_filter_strategy import LLMContentFilter
async def main():
#…
filter = LLMContentFilter(
instruction="""
Extract the main educational content while preserving its original wording and substance completely.
1. Maintain the exact language and…
filter = LLMContentFilter(
instruction="""
Focus on extracting specific types of content:
- Technical documentation
- Code examples
- API references
Reformat the content into…
6. Using Fit Markdown
When a content filter is active, the library produces two forms of markdown inside result.markdown:
raw_markdown: The full unfiltered markdown.fit_markdown: A “fit” version…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
from crawl4ai.content_filter_strategy import…
7. The `MarkdownGenerationResult` Object
If your library stores detailed markdown output in an object like MarkdownGenerationResult, you’ll see fields such as:
raw_markdown: The direct HTML-to-markdown transformation (no…
md_obj = result.markdown # your library’s naming may vary
print("RAW:\n", md_obj.raw_markdown)
print("CITED:\n", md_obj.markdown_with_citations)
print("REFERENCES:\n",…
8. Combining Filters (BM25 + Pruning) in Two Passes
You might want to prune out noisy boilerplate first (with PruningContentFilter), and then rank what’s left against a user query (with BM25ContentFilter). You don’t have to crawl the page…
Two-Pass Example
What’s Happening?
- Raw HTML: We crawl once and store the raw HTML in
result.html. - PruningContentFilter: Takes HTML + optional parameters. It extracts blocks of text or partial…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_filter_strategy import PruningContentFilter, BM25ContentFilter
from bs4 import BeautifulSoup
async def…
Tips & Variations
- Plain Text vs. HTML: If your pruned output is mostly text, BM25 can still handle it; just keep in mind it expects a valid string input. If you supply partial HTML (like
"<p>some text</p>"),…
One-Pass Combination?
If your codebase or pipeline design allows applying multiple filters in one pass, you could do so. But often it’s simpler—and more transparent—to run them sequentially, analyzing each step’s…
9. Common Pitfalls & Tips
- No Markdown Output?
- Make sure the crawler actually retrieved HTML. If the site is heavily JS-based, you may need to enable dynamic rendering or wait for elements.
- Check if your…
10. Summary & Next Steps
In this Markdown Generation Basics tutorial, you learned to:
- Configure the DefaultMarkdownGenerator with HTML-to-text options.
- Select different HTML sources using the
content_source…
Extraction & Structured Data
Ask AI - Crawl4AI Documentation (v0.9.x)
This page documents the Ask AI feature in Crawl4AI, which enables users to ask questions about crawled web content using LLM providers. It covers the ask_ai method, LLM configuration, and usage…
Overview
The Ask AI feature in Crawl4AI allows you to ask questions about the content you have crawled. Instead of manually parsing and analyzing the extracted content, you can leverage Large Language Models…
Basic Usage
To use Ask AI, you first need to crawl a page and then pass the extracted content along with your question to the ask_ai method. The method requires an LLMConfig object that specifies which LLM…
import asyncio
from crawl4ai import AsyncWebCrawler, LLMConfig
async def main():
llm_config = LLMConfig(provider="openai/gpt-4o", api_token="your-api-token")
async with AsyncWebCrawler() as…
LLM Configuration
The LLMConfig class is used to configure the LLM provider for Ask AI. It supports a wide range of providers through the litellm library, including OpenAI, Anthropic, Google Gemini, Azure OpenAI, and…
from crawl4ai import LLMConfig
# OpenAI
llm_config = LLMConfig(
provider="openai/gpt-4o",
api_token="your-openai-api-token"
)
# Anthropic
llm_config = LLMConfig(…
Advanced Usage
You can customize the behavior of the LLM by providing a system prompt, adjusting the temperature, and setting the maximum number of tokens in the response. This allows you to tailor the AI's…
answer = await crawler.ask_ai(
question="Extract all product names and prices from this page.",
context=result.markdown,
llm_config=llm_config,
system_prompt="You are a helpful…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| question | str | The question you want to ask about the crawled content. | Yes | |
| context | str | The crawled content (typically result.markdown) that provides context for the question. | Yes | |
| llm_config | LLMConfig | Configuration object specifying the LLM provider, API token, and model parameters. | Yes | |
| system_prompt | str | Optional system prompt to guide the LLM's behavior and response format. | None | No |
| temperature | float | Controls the randomness of the LLM's output. Lower values produce more deterministic responses. | 0.7 | No |
| max_tokens | int | Maximum number of tokens to generate in the response. | 1024 | No |
Fit Markdown with Pruning & BM25
Explains how to use Fit Markdown with Pruning and BM25 content filters in Crawl4AI to extract concise, relevant content from web pages.
Overview
Fit Markdown is a specialized filtered version of your page’s markdown, focusing on the most relevant content. By default, Crawl4AI converts the entire HTML into a broad raw_markdown.…
1. How “Fit Markdown” Works
1.1 The `content_filter`
In CrawlerRunConfig, you can specify a content_filter to shape how content is pruned or ranked before final markdown generation. A filter’s logic is applied before or during the…
1.2 Common Filters
- PruningContentFilter – Scores each node by text density, link density, and tag importance, discarding those below a threshold.
- BM25ContentFilter – Focuses on textual relevance using…
2. PruningContentFilter
Pruning discards less relevant nodes based on text density, link density, and tag importance. It’s a heuristic-based approach—if certain sections appear too “thin” or too “spammy,” they’re…
2.1 Usage Example
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_filter_strategy import PruningContentFilter
from crawl4ai.markdown_generation_strategy import…
2.2 Key Parameters
min_word_threshold(int): If a block has fewer words than this, it’s pruned.threshold_type(str):"fixed"→ each node must exceedthreshold(0–1)."dynamic"→ node…
3. BM25ContentFilter
BM25 is a classical text ranking algorithm often used in search engines. If you have a user query or rely on page metadata to derive a query, BM25 can identify which text chunks best match…
3.1 Usage Example
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_filter_strategy import BM25ContentFilter
from crawl4ai.markdown_generation_strategy import…
3.2 Parameters
user_query(str, optional): E.g."machine learning". If blank, the filter tries to glean a query from page metadata.bm25_threshold(float, default 1.0):- Higher → fewer chunks…
4. Accessing the “Fit” Output
After the crawl, your “fit” content is found in result.markdown.fit_markdown.
If the content filter is BM25, you might see additional logic or references in fit_markdown that highlight…
fit_md = result.markdown.fit_markdown
fit_html = result.markdown.fit_html
5. Code Patterns Recap
5.1 Pruning
prune_filter = PruningContentFilter(
threshold=0.5,
threshold_type="fixed",
min_word_threshold=10
)
md_generator = DefaultMarkdownGenerator(content_filter=prune_filter)
config =…
5.2 BM25
bm25_filter = BM25ContentFilter(
user_query="health benefits fruit",
bm25_threshold=1.2
)
md_generator = DefaultMarkdownGenerator(content_filter=bm25_filter)
config =…
6. Combining with “word_count_threshold” & Exclusions
Remember you can also specify:
Thus, multi-level filtering occurs:
- The crawler’s
excluded_tagsare removed from the HTML first. - The content filter (Pruning, BM25, or custom) prunes or…
config = CrawlerRunConfig(
word_count_threshold=10,
excluded_tags=["nav", "footer", "header"],
exclude_external_links=True,
markdown_generator=DefaultMarkdownGenerator(…
7. Custom Filters
If you need a different approach (like a specialized ML model or site-specific heuristics), you can create a new class inheriting from RelevantContentFilter and implement filter_content(html).…
from crawl4ai.content_filter_strategy import RelevantContentFilter
class MyCustomFilter(RelevantContentFilter):
def filter_content(self, html, min_word_threshold=None):
# parse HTML,…
8. Final Thoughts
Fit Markdown is a crucial feature for:
- Summaries: Quickly get the important text from a cluttered page.
- Search: Combine with BM25 to produce content relevant to a query.
- **AI…
Table Extraction Strategies - Crawl4AI Documentation (v0.9.x)
This page covers Crawl4AI's table extraction strategies, including the default algorithm, LLM-based extraction, and custom strategies. It explains the strategy design pattern, configuration options,…
Overview
New in v0.7.3+ : Table extraction now follows the Strategy Design Pattern , providing unprecedented flexibility and power for handling different table structures. Don't worry - **your…
What's Changed?
- Architecture : Table extraction now uses pluggable strategies
- Backward Compatible : Your existing code with
table_score_thresholdcontinues to work - More Power : Choose from…
Key Points
✅ Old code still works - No breaking changes ✅ Same default behavior - Uses the proven extraction algorithm ✅ New capabilities - Add LLM extraction or custom strategies when needed ✅…
Quick Start
The Simplest Way (Works Like Before)
If you're already using Crawl4AI, nothing changes:
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def extract_tables():
async with AsyncWebCrawler() as crawler:
# This works exactly like before - uses…
Using the Old Configuration (Still Supported)
Your existing code with table_score_threshold continues to work:
# This old approach STILL WORKS - we maintain backward compatibility
config = CrawlerRunConfig(
table_score_threshold=7 # Internally creates…
Table Extraction Strategies
Understanding the Strategy Pattern
The strategy pattern allows you to choose different table extraction algorithms at runtime. Think of it as having different tools in a toolbox - you pick the right one for the job:
- **No explicit…
Available Strategies
| Strategy | Description | Use Case | Cost | When to Use |
|---|---|---|---|---|
DefaultTableExtraction |
RECOMMENDED : Same algorithm as before v0.7.3 | General purpose (default) | … |
DefaultTableExtraction
The default strategy uses a sophisticated scoring system to identify data tables:
from crawl4ai import DefaultTableExtraction, CrawlerRunConfig
# Customize the default extraction
table_strategy = DefaultTableExtraction(
table_score_threshold=7, # Scoring threshold (default:…
Scoring System
The scoring system evaluates multiple factors:
| Factor | Score Impact | Description |
|---|---|---|
Has <thead> |
+2 | Semantic table structure |
Has <tbody> |
+1 | Organized table… |
LLMTableExtraction (Use Sparingly!)
⚠️ WARNING : Only use this when DefaultTableExtraction fails with complex tables!
LLMTableExtraction uses AI to understand complex table structures that traditional parsers struggle with. It…
from crawl4ai import LLMTableExtraction, LLMConfig, CrawlerRunConfig
# Configure LLM (costs money per call!)
llm_config = LLMConfig(
provider="groq/llama-3.3-70b-versatile", # Fast provider for…
When to Use LLMTableExtraction
✅ Use ONLY when :
- Tables have complex merged cells (rowspan/colspan) that break DefaultTableExtraction
- Nested tables that need semantic understanding
- Tables with irregular structures -…
How Smart Chunking Works
LLMTableExtraction automatically handles large tables through intelligent chunking:
- Automatic Detection : Tables exceeding the token threshold are automatically split
- Smart Splitting :…
Performance Optimization for LLMTableExtraction
Provider Recommendations by Table Size :
| Table Size | Recommended Providers | Why |
|---|---|---|
| Small (<50 rows) | Any provider | Fast enough |
| Medium (50-200 rows) | Groq,… |
NoTableExtraction
Disable table extraction for better performance when tables aren't needed:
from crawl4ai import NoTableExtraction, CrawlerRunConfig
config = CrawlerRunConfig(
table_extraction=NoTableExtraction()
)
# Tables won't be extracted, improving performance
result = await…
Extracted Table Structure
Each extracted table contains:
{
"headers": ["Column 1", "Column 2", ...], # Column headers
"rows": [ # Data rows
["Row 1 Col 1", "Row 1 Col 2", ...],
["Row 2 Col 1", "Row…
Configuration Options
Basic Configuration
config = CrawlerRunConfig(
# Table extraction settings
table_score_threshold=7, # Default threshold (backward compatible)
table_extraction=strategy, # Optional: custom strategy…
Advanced Configuration
from crawl4ai import DefaultTableExtraction, CrawlerRunConfig
# Fine-tuned extraction
strategy = DefaultTableExtraction(
table_score_threshold=5, # Lower = more permissive
min_rows=3,…
Working with Extracted Tables
Convert to Pandas DataFrame
import pandas as pd
async def tables_to_dataframes(url):
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(url)
dataframes = []
for table_data in…
Filter Tables by Criteria
async def extract_large_tables(url):
async with AsyncWebCrawler() as crawler:
# Configure minimum size requirements
strategy = DefaultTableExtraction(
min_rows=10,…
Export Tables to Different Formats
import json
import csv
async def export_tables(url):
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(url)
for i, table in enumerate(result.tables):…
Creating Custom Strategies
Extend TableExtractionStrategy to create custom extraction logic:
Example: Financial Table Extractor
from crawl4ai import TableExtractionStrategy
from typing import List, Dict, Any
import re
class FinancialTableExtractor(TableExtractionStrategy):
"""Extract tables containing financial…
Example: Specific Table Extractor
class SpecificTableExtractor(TableExtractionStrategy):
"""Extract only tables matching specific criteria."""
def __init__(self,
required_headers=None,…
Combining with Other Strategies
Table extraction works seamlessly with other Crawl4AI strategies:
from crawl4ai import (
AsyncWebCrawler,
CrawlerRunConfig,
DefaultTableExtraction,
LLMExtractionStrategy,
JsonCssExtractionStrategy
)
async def combined_extraction(url):
async…
Performance Considerations
Optimization Tips
- Disable when not needed : Use
NoTableExtractionif tables aren't required - Target specific areas : Use
css_selectorto limit processing scope - Set minimum thresholds : Filter out…
# Optimized configuration for large pages
config = CrawlerRunConfig(
# Only process main content area
css_selector="article.main-content",
# Exclude navigation and sidebars…
Migration Guide
Important: Your Code Still Works!
No changes required! The transition to the strategy pattern is fully backward compatible .
How It Works Internally
v0.7.2 and Earlier
# Old way - directly passing table_score_threshold
config = CrawlerRunConfig(
table_score_threshold=7
)
# Internally: No strategy pattern, direct implementation
v0.7.3+ (Current)
# Old way STILL WORKS - we handle it internally
config = CrawlerRunConfig(
table_score_threshold=7
)
# Internally: Automatically creates DefaultTableExtraction(table_score_threshold=7)
Taking Advantage of New Features
While your old code works, you can now use the strategy pattern for more control:
# Option 1: Keep using the old way (perfectly fine!)
config = CrawlerRunConfig(
table_score_threshold=7 # Still supported
)
# Option 2: Use the new strategy pattern (more flexibility)
from…
Summary
- ✅ No breaking changes - Old code works as-is
- ✅ Same defaults - DefaultTableExtraction is automatically used
- ✅ Gradual adoption - Use new features when you need them
- ✅ **Full…
Best Practices
1. Choose the Right Strategy (Cost-Conscious Approach)
Decision Flow :
Strategy Selection Guide :
- DefaultTableExtraction : Use for 99% of cases - it's free and effective
- LLMTableExtraction : Only for complex tables with merged cells…
1. Do you need tables?
→ No: Use NoTableExtraction
→ Yes: Continue to #2
2. Try DefaultTableExtraction first (FREE)
→ Works? Done! ✅
→ Fails? Continue to #3
3. Is the table critical…
2. Validate Extracted Data
def validate_table(table):
"""Validate table data quality."""
# Check structure
if not table.get('rows'):
return False
# Check consistency
if table.get('headers'):…
3. Handle Edge Cases
async def robust_table_extraction(url):
"""Extract tables with error handling."""
async with AsyncWebCrawler() as crawler:
try:
config = CrawlerRunConfig(…
Troubleshooting
Common Issues and Solutions
| Issue | Cause | Solution |
|---|---|---|
| No tables extracted | Score too high | Lower table_score_threshold |
| Layout tables included | Score too low | Increase table_score_threshold… |
Debug Logging
Enable verbose logging to understand extraction decisions:
import logging
# Configure logging
logging.basicConfig(level=logging.DEBUG)
# Enable verbose mode in strategy
strategy = DefaultTableExtraction(
table_score_threshold=7,
verbose=True #…
See Also
- Extraction Strategies - Overview of all extraction strategies
- Content Selection - Using CSS selectors and filters
- [Performance…
Chunking Strategies
This page explains various chunking strategies for dividing large texts into manageable parts, including regex-based, sentence-based, topic-based, fixed-length word, and sliding window chunking, as…
Chunking Strategies
Chunking strategies are critical for dividing large texts into manageable parts, enabling effective content processing and extraction. These strategies are foundational in cosine similarity-based…
Why Use Chunking?
- Cosine Similarity and Query Relevance: Prepares chunks for semantic similarity analysis.
- RAG System Integration: Seamlessly processes and stores chunks for retrieval.
- **Structured…
Methods of Chunking
1. Regex-Based Chunking
Splits text based on regular expression patterns, useful for coarse segmentation.
Code Example:
class RegexChunking:
def __init__(self, patterns=None):
self.patterns = patterns or [r'\n\n'] # Default pattern for paragraphs
def chunk(self, text):
paragraphs = [text]…
2. Sentence-Based Chunking
Divides text into sentences using NLP tools, ideal for extracting meaningful statements.
Code Example:
from nltk.tokenize import sent_tokenize
class NlpSentenceChunking:
def chunk(self, text):
sentences = sent_tokenize(text)
return [sentence.strip() for sentence in sentences]
#…
3. Topic-Based Segmentation
Uses algorithms like TextTiling to create topic-coherent chunks.
Code Example:
from nltk.tokenize import TextTilingTokenizer
class TopicSegmentationChunking:
def __init__(self):
self.tokenizer = TextTilingTokenizer()
def chunk(self, text):
return…
4. Fixed-Length Word Chunking
Segments text into chunks of a fixed word count.
Code Example:
class FixedLengthWordChunking:
def __init__(self, chunk_size=100):
self.chunk_size = chunk_size
def chunk(self, text):
words = text.split()
return [' '.join(words[i:i…
5. Sliding Window Chunking
Generates overlapping chunks for better contextual coherence.
Code Example:
class SlidingWindowChunking:
def __init__(self, window_size=100, step=50):
self.window_size = window_size
self.step = step
def chunk(self, text):
words =…
Combining Chunking with Cosine Similarity
To enhance the relevance of extracted content, chunking strategies can be paired with cosine similarity techniques. Here’s an example workflow:
Code Example:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
class CosineSimilarityExtractor:
def __init__(self, query):
self.query…
Clustering Strategies - Crawl4AI Documentation (v0.9.x)
This page provides a comprehensive guide to using the Cosine Strategy in Crawl4AI for semantic content extraction, covering configuration options, usage examples, best practices, and error handling.
Cosine Strategy
The Cosine Strategy in Crawl4AI uses similarity-based clustering to identify and extract relevant content sections from web pages. This strategy is particularly useful when you need to find and…
How It Works
The Cosine Strategy:
- Breaks down page content into meaningful chunks
- Converts text into vector representations
- Calculates similarity between chunks
- Clusters similar content together 5.…
Basic Usage
from crawl4ai import CosineStrategy
strategy = CosineStrategy(
semantic_filter="product reviews", # Target content type
word_count_threshold=10, # Minimum words per cluster…
Configuration Options
Core Parameters
CosineStrategy(
# Content Filtering
semantic_filter: str = None, # Keywords/topic for content filtering
word_count_threshold: int = 10, # Minimum words per cluster…
Parameter Details
- semantic_filter
- Sets the target topic or content type
- Use keywords relevant to your desired content
- Example: "technical specifications", "user reviews", "pricing…
# Strict matching
strategy = CosineStrategy(sim_threshold=0.8)
# Loose matching
strategy = CosineStrategy(sim_threshold=0.3)
# Only consider substantial paragraphs
strategy = CosineStrategy(word_count_threshold=50)
# Get top 5 most relevant content clusters
strategy = CosineStrategy(top_k=5)
Use Cases
1. Article Content Extraction
strategy = CosineStrategy(
semantic_filter="main article content",
word_count_threshold=100, # Longer blocks for articles
top_k=1 # Usually want single main…
2. Product Review Analysis
strategy = CosineStrategy(
semantic_filter="customer reviews and ratings",
word_count_threshold=20, # Reviews can be shorter
top_k=10, # Get multiple reviews…
3. Technical Documentation
strategy = CosineStrategy(
semantic_filter="technical specifications documentation",
word_count_threshold=30,
sim_threshold=0.6, # Stricter matching for technical content…
Advanced Features
Custom Clustering
strategy = CosineStrategy(
linkage_method='complete', # Alternative clustering method
max_dist=0.4, # Larger clusters…
Content Filtering Pipeline
strategy = CosineStrategy(
semantic_filter="pricing plans features",
word_count_threshold=15,
sim_threshold=0.5,
top_k=3
)
async def extract_pricing_features(url: str):
async…
Best Practices
-
Adjust Thresholds Iteratively
- Start with default values
- Adjust based on results
- Monitor clustering quality
-
Choose Appropriate Word Count Thresholds
- Higher for…
strategy = CosineStrategy(
word_count_threshold=10, # Filter early
top_k=5, # Limit results
verbose=True # Monitor performance
)
# For mixed content pages
strategy = CosineStrategy(
semantic_filter="product features",
sim_threshold=0.4, # More flexible matching
max_dist=0.3, # Larger clusters…
Error Handling
try:
result = await crawler.arun(
url="https://example.com",
extraction_strategy=strategy
)
if result.success:
content = json.loads(result.extracted_content)…
Conclusion
The Cosine Strategy is particularly effective when:
- Content structure is inconsistent
- You need semantic understanding
- You want to find similar content blocks
- Structure-based extraction…
Extracting JSON (LLM)
A guide to using Crawl4AI's LLM-based extraction strategy to extract structured JSON from web pages using any LLM via LiteLLM, including schema definition, chunking, input formats, and practical…
Overview
In some cases, you need to extract complex or unstructured information from a webpage that a simple CSS/XPath schema cannot easily parse. Or you want AI-driven insights, classification, or…
1. Why Use an LLM?
- Complex Reasoning: If the site’s data is unstructured, scattered, or full of natural language context.
- Semantic Extraction: Summaries, knowledge graphs, or relational data that require…
2. Provider-Agnostic via LiteLLM
You can use LLMConfig, to quickly configure multiple variations of LLMs and experiment with them to find the optimal one for your use case. You can read more about LLMConfig…
llm_config = LLMConfig(provider="openai/gpt-4o-mini", api_token=os.getenv("OPENAI_API_KEY"))
3. How LLM Extraction Works
3.1 Flow
- Chunking (optional): The HTML or markdown is split into smaller segments if it’s very long (based on
chunk_token_threshold, overlap, etc.). - Prompt Construction: For each chunk, the…
3.2 `extraction_type`
"schema": The model tries to return JSON conforming to your Pydantic-based schema."block": The model returns freeform text, or smaller JSON structures, which the library…
4. Key Parameters
Below is an overview of important LLM extraction parameters. All are typically set inside LLMExtractionStrategy(...). You then put that strategy in your `CrawlerRunConfig(...,…
extraction_strategy = LLMExtractionStrategy(
llm_config = LLMConfig(provider="openai/gpt-4", api_token="YOUR_OPENAI_KEY"),
schema=MyModel.model_json_schema(),
extraction_type="schema",…
5. Putting It in `CrawlerRunConfig`
Important: In Crawl4AI, all strategy definitions should go inside the CrawlerRunConfig, not directly as a param in arun(). Here’s a full example:
import os
import asyncio
import json
from pydantic import BaseModel, Field
from typing import List
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode, LLMConfig
from…
6. Chunking Details
6.1 `chunk_token_threshold`
If your page is large, you might exceed your LLM’s context window. chunk_token_threshold sets the approximate max tokens per chunk. The library calculates word→token ratio using…
6.2 `overlap_rate`
To keep context continuous across chunks, we can overlap them. E.g., overlap_rate=0.1 means each subsequent chunk includes 10% of the previous chunk’s text. This is helpful if your needed info…
6.3 Performance & Parallelism
By chunking, you can potentially process multiple chunks in parallel (depending on your concurrency settings and the LLM provider). This reduces total time if the site is huge or has many sections.
7. Input Format
By default, LLMExtractionStrategy uses input_format="markdown", meaning the crawler’s final markdown is fed to the LLM. You can change to:
html: The cleaned HTML or raw HTML…
LLMExtractionStrategy(
# ...
input_format="html", # Instead of "markdown" or "fit_markdown"
)
8. Token Usage & Show Usage
To keep track of tokens and cost, each chunk is processed with an LLM call. We record usage in:
usages(list): token usage per chunk or call.total_usage: sum of all chunk calls. -…
llm_strategy = LLMExtractionStrategy(...)
# ...
llm_strategy.show_usage()
# e.g. “Total usage: 1241 tokens across 2 chunk calls”
9. Example: Building a Knowledge Graph
Below is a snippet combining LLMExtractionStrategy with a Pydantic schema for a knowledge graph. Notice how we pass an instruction telling the model what to parse.
**Key…
import os
import json
import asyncio
from typing import List
from pydantic import BaseModel, Field
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode, LLMConfig
from…
10. Best Practices & Caveats
- Cost & Latency: LLM calls can be slow or expensive. Consider chunking or smaller coverage if you only need partial data.
- Model Token Limits: If your page + instruction exceed the…
11. Conclusion
LLM-based extraction in Crawl4AI is provider-agnostic, letting you choose from hundreds of models via LiteLLM. It’s perfect for semantically complex tasks or generating advanced…
Extracting JSON (No LLM)
This page covers Crawl4AI's LLM-free extraction strategies, including schema-based extraction with CSS/XPath selectors (JsonCssExtractionStrategy, JsonXPathExtractionStrategy) and regex-based…
1. Intro to Schema-Based Extraction
A schema defines:
- A base selector that identifies each "container" element on the page (e.g., a product row, a blog post card).
- Fields describing which CSS/XPath selectors to use for…
2. Simple Example: Crypto Prices
Let's begin with a simple schema-based extraction using the JsonCssExtractionStrategy. Below is a snippet that extracts cryptocurrency prices from a site (similar to the legacy Coinbase…
import json
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def extract_crypto_prices():
# 1. Define a…
import json
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai import JsonXPathExtractionStrategy
async def extract_crypto_prices_xpath():
# 1. Minimal dummy…
3. Advanced Schema & Nested Structures
Real sites often have nested or repeated data—like categories containing products, which themselves have a list of reviews or features. For that, we can define nested or list (and even…
schema = {
"name": "E-commerce Product Catalog",
"baseSelector": "div.category",
# (1) We can define optional baseFields if we want to extract attributes
# from the category…
import json
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai import JsonCssExtractionStrategy
ecommerce_schema = {
# ... the advanced schema from above…
4. RegexExtractionStrategy - Fast Pattern-Based Extraction
Crawl4AI now offers a powerful new zero-LLM extraction strategy: RegexExtractionStrategy. This strategy provides lightning-fast extraction of common data types like emails, phone numbers, URLs,…
import json
import asyncio
from crawl4ai import (
AsyncWebCrawler,
CrawlerRunConfig,
RegexExtractionStrategy
)
async def extract_with_regex():
# Create a strategy using built-in…
# Use individual patterns
strategy = RegexExtractionStrategy(pattern=RegexExtractionStrategy.Email)
# Combine multiple patterns
strategy = RegexExtractionStrategy(
pattern = (…
import json
import asyncio
from crawl4ai import (
AsyncWebCrawler,
CrawlerRunConfig,
RegexExtractionStrategy
)
async def extract_prices():
# Define a custom pattern for US Dollar…
import json
import asyncio
from pathlib import Path
from crawl4ai import (
AsyncWebCrawler,
CrawlerRunConfig,
RegexExtractionStrategy,
LLMConfig
)
async def…
[
{
"url": "https://example.com",
"label": "email",
"value": "contact@example.com",
"span": [145, 163]
},
{
"url": "https://example.com",
"label": "url",
"value":…
5. Why "No LLM" Is Often Better
- Zero Hallucination: Pattern-based extraction doesn't guess text. It either finds it or not.
- Guaranteed Structure: The same schema or regex yields consistent JSON across many pages, so…
6. Base Element Attributes & Additional Fields
It's easy to extract attributes (like href, src, or data-xxx) from your base or nested elements using:
{
"name": "href",
"type": "attribute",
"attribute": "href",…
{
"name": "href",
"type": "attribute",
"attribute": "href",
"default": null
}
7. Putting It All Together: Larger Example
Consider a blog site. We have a schema that extracts the URL from each post card (via baseFields with an "attribute": "href"), plus the title, date, summary, and author:
schema = {…
schema = {
"name": "Blog Posts",
"baseSelector": "a.blog-post-card",
"baseFields": [
{"name": "post_url", "type": "attribute", "attribute": "href"}
],
"fields": [
{"name": "title",…
8. Extracting Sibling Data with `source`
Some websites split a single logical item across sibling elements rather than nesting everything inside one container. A classic example is Hacker News, where each submission spans two adjacent…
<tr class="athing submission"> <!-- rank, title, url -->
<td><span class="rank">1.</span></td>
<td><span class="titleline"><a href="https://example.com">Example Title</a></span></td>
</tr>
<tr>…
schema = {
"name": "HN Submissions",
"baseSelector": "tr.athing.submission",
"fields": [
{"name": "rank", "selector": "span.rank", "type": "text"},
{"name": "title",…
9. Tips & Best Practices
- Inspect the DOM in Chrome DevTools or Firefox's Inspector to find stable selectors.
- Start Simple: Verify you can extract a single field. Then add complexity like nested objects or…
10. Schema Generation Utility
While manually crafting schemas is powerful and precise, Crawl4AI now offers a convenient utility to automatically generate extraction schemas using LLM. This is particularly useful when:
-…
from crawl4ai import JsonCssExtractionStrategy, JsonXPathExtractionStrategy
from crawl4ai import LLMConfig
# Sample HTML with product information
html = """
<div class="product-card">
<h2…
# Default: validated (recommended)
schema = JsonCssExtractionStrategy.generate_schema(
url="https://news.ycombinator.com",
query="Extract each story: title, url, score, author",
)
# Skip…
from crawl4ai import JsonCssExtractionStrategy
from crawl4ai.models import TokenUsage
usage = TokenUsage()
schema = JsonCssExtractionStrategy.generate_schema(…
usage = TokenUsage()
schema1 = JsonCssExtractionStrategy.generate_schema(url=url1, query=q1, usage=usage)
schema2 = JsonCssExtractionStrategy.generate_schema(url=url2, query=q2,…
from crawl4ai import JsonCssExtractionStrategy, LLMConfig
# Collect HTML samples from different pages
html_sample_1 = """
<table class="specs">
<tr><td>Brand</td><td>Apple</td></tr>…
11. Conclusion
With Crawl4AI's LLM-free extraction strategies - JsonCssExtractionStrategy, JsonXPathExtractionStrategy, and now RegexExtractionStrategy - you can build powerful pipelines that:
- Scrape any…
Advanced Features
Adaptive Strategies - Crawl4AI Documentation (v0.9.x)
This guide covers advanced adaptive strategies in Crawl4AI, including the three-layer scoring system, link ranking algorithm, domain-specific configurations, performance optimization, debugging,…
Overview
While the default adaptive crawling configuration works well for most use cases, understanding the underlying strategies and scoring mechanisms allows you to fine-tune the crawler for specific…
The Three-Layer Scoring System
1. Coverage Score
Coverage measures how comprehensively your knowledge base covers the query terms and related concepts.
Mathematical Foundation
Coverage(K, Q) = Σ(t ∈ Q) score(t, K) / |Q|
where score(t, K) = doc_coverage(t) × (1 + freq_boost(t))
Components
- Document Coverage: Percentage of documents containing the term
- Frequency Boost: Logarithmic bonus for term frequency
- Query Decomposition: Handles multi-word queries intelligently
Tuning Coverage
# For technical documentation with specific terminology
config = AdaptiveConfig(
confidence_threshold=0.85, # Require high coverage
top_k_links=5 # Cast wider net
)
# For…
2. Consistency Score
Consistency evaluates whether the information across pages is coherent and non-contradictory.
How It Works
- Extracts key statements from each document
- Compares statements across documents
- Measures agreement vs. contradiction
- Returns normalized score (0-1)
Practical Impact
- High consistency (>0.8): Information is reliable and coherent
- Medium consistency (0.5-0.8): Some variation, but generally aligned
- Low consistency (<0.5): Conflicting information,…
3. Saturation Score
Saturation detects when new pages stop providing novel information.
Detection Algorithm
# Tracks new unique terms per page
new_terms_page_1 = 50
new_terms_page_2 = 30 # 60% of first
new_terms_page_3 = 15 # 50% of second
new_terms_page_4 = 5 # 33% of third
# Saturation detected:…
Configuration
config = AdaptiveConfig(
min_gain_threshold=0.1 # Stop if <10% new information
)
Link Ranking Algorithm
Expected Information Gain
Each uncrawled link is scored based on:
ExpectedGain(link) = Relevance × Novelty × Authority
1. Relevance Scoring
Uses BM25 algorithm on link preview text:
Factors:
- Term frequency in preview
- Inverse document frequency
- Preview length normalization
relevance = BM25(link.preview_text, query)
2. Novelty Estimation
Measures how different the link appears from already-crawled content:
Prevents crawling duplicate or highly similar pages.
novelty = 1 - max_similarity(preview, knowledge_base)
3. Authority Calculation
URL structure and domain analysis:
Factors:
- Domain reputation
- URL depth (fewer slashes = higher authority)
- Clean URL structure
authority = f(domain_rank, url_depth, url_structure)
Domain-Specific Configurations
Technical Documentation
Rationale:
- High threshold ensures comprehensive coverage
- Lower gain threshold captures edge cases
- Moderate link following for depth
tech_doc_config = AdaptiveConfig(
confidence_threshold=0.85,
max_pages=30,
top_k_links=3,
min_gain_threshold=0.05 # Keep crawling for small gains
)
News & Articles
Rationale:
- Lower threshold (articles often repeat information)
- Higher gain threshold (avoid duplicate stories)
- More links per page (explore different perspectives)
news_config = AdaptiveConfig(
confidence_threshold=0.6,
max_pages=10,
top_k_links=5,
min_gain_threshold=0.15 # Stop quickly on repetition
)
E-commerce
Rationale:
- Balanced threshold for product variations
- Focused link following (avoid infinite products)
- Standard gain threshold
ecommerce_config = AdaptiveConfig(
confidence_threshold=0.7,
max_pages=20,
top_k_links=2,
min_gain_threshold=0.1
)
Research & Academic
Rationale:
- Very high threshold for completeness
- Many pages allowed for thorough research
- Very low gain threshold to capture references
research_config = AdaptiveConfig(
confidence_threshold=0.9,
max_pages=50,
top_k_links=4,
min_gain_threshold=0.02 # Very low - capture citations
)
Performance Optimization
Memory Management
# For large crawls, use streaming
config = AdaptiveConfig(
max_pages=100,
save_state=True,
state_path="large_crawl.json"
)
# Periodically clean state
if len(state.knowledge_base) >…
Parallel Processing
# Use multiple start points
start_urls = [
"https://docs.example.com/intro",
"https://docs.example.com/api",
"https://docs.example.com/guides"
]
# Crawl in parallel
tasks = […
Debugging & Analysis
Enable Debugging
import logging
logging.basicConfig(level=logging.DEBUG)
adaptive = AdaptiveCrawler(crawler, config, verbose=True)
Analyze Crawl Patterns
# After crawling
state = await adaptive.digest(start_url, query)
# Analyze link selection
print("Link selection order:")
for i, url in enumerate(state.crawl_order):
print(f"{i+1}. {url}")
#…
Export for Analysis
# Export detailed metrics
import json
metrics = {
"query": query,
"total_pages": len(state.crawled_urls),
"confidence": adaptive.confidence,
"coverage_stats":…
Custom Strategies
Implementing a Custom Strategy
from crawl4ai.adaptive_crawler import CrawlStrategy
class DomainSpecificStrategy(CrawlStrategy):
def calculate_coverage(self, state: CrawlState) -> float:
# Custom coverage calculation…
Combining Strategies
class HybridStrategy(CrawlStrategy):
def __init__(self):
self.strategies = [
TechnicalDocStrategy(),
SemanticSimilarityStrategy(),…
Best Practices
1. Start Conservative
Begin with default settings and adjust based on results:
# Start with defaults
result = await adaptive.digest(url, query)
# Analyze and adjust
if adaptive.confidence < 0.7:
config.max_pages += 10
config.confidence_threshold -= 0.1
2. Monitor Resource Usage
import psutil
# Check memory before large crawls
memory_percent = psutil.virtual_memory().percent
if memory_percent > 80:
config.max_pages = min(config.max_pages, 20)
3. Use Domain Knowledge
# For API documentation
if "api" in start_url:
config.top_k_links = 2 # APIs have clear structure
# For blogs
if "blog" in start_url:
config.min_gain_threshold = 0.2 # Avoid similar posts
4. Validate Results
# Always validate the knowledge base
relevant_content = adaptive.get_relevant_content(top_k=10)
# Check coverage
query_terms = set(query.lower().split())
covered_terms = set()
for doc in…
Next Steps
Explore Custom Strategy Implementation Learn about Knowledge Base Management See [Performance…
Anti-Bot Detection & Fallback
Explains how Crawl4AI detects anti-bot blocks and describes the layered retry/fallback system using proxies, retries, and custom fetch functions to retrieve content from protected sites.
How Detection Works
After each crawl attempt, Crawl4AI inspects the HTTP status code and HTML content for known anti-bot signals:
- HTTP 403/429 with short or empty response bodies
- Challenge pages —…
Configuration Options
All anti-bot retry options live on CrawlerRunConfig:
| Parameter | Type | Default | Description |
|---|---|---|---|
proxy_config |
ProxyConfig, list[ProxyConfig], or None |
… |
Escalation Chain
Each retry round tries every proxy in proxy_config in order. If all rounds are exhausted and the page is still blocked, the fallback fetch function is called as a last resort.
Worst-case attempts…
For each round (1 + max_retries rounds):
1. Try proxy_config[0] (or direct if proxy_config is None)
2. If blocked → try proxy_config[1]
3. If blocked → try proxy_config[2]
4. ...…
Crawl Stats
Every crawl result includes a crawl_stats dict with detailed attempt tracking:
result.crawl_stats = {
"attempts": 3, # total browser attempts made
"retries": 1, # retry rounds used (0 = succeeded first round)
"proxies_used": […
Usage Examples
Simple Retry (No Proxy)
Retry the crawl up to 3 times when blocking is detected. Useful when blocks are intermittent or IP-based.
from crawl4ai import AsyncWebCrawler
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig
async with AsyncWebCrawler(config=BrowserConfig(headless=True)) as crawler:
result = await…
Single Proxy
Pass a single ProxyConfig — it's used on every attempt. Same behavior as always.
from crawl4ai.async_configs import ProxyConfig
config = CrawlerRunConfig(
max_retries=2,
proxy_config=ProxyConfig(
server="http://proxy.example.com:8080",
username="user",…
Direct-First, Then Proxies
Try without a proxy first, then escalate to proxies if blocked. Use ProxyConfig.DIRECT (or the string "direct") in the list to represent a no-proxy attempt.
With this setup, each round tries…
config = CrawlerRunConfig(
max_retries=1,
proxy_config=[
ProxyConfig.DIRECT, # Try without proxy first
ProxyConfig(…
Proxy List (Escalation)
Pass a list of proxies. They're tried in order — first one that works wins. Within each retry round, the entire list is tried again.
With this setup, each round tries the datacenter proxy first,…
config = CrawlerRunConfig(
max_retries=1,
proxy_config=[
ProxyConfig(
server="http://datacenter-proxy.example.com:8080",
username="user",…
Fallback Fetch Function
When all browser-based attempts fail, call a custom async function as a last resort. This function receives the URL and must return raw HTML as a string. The returned HTML is processed through the…
import aiohttp
async def my_scraping_api(url: str) -> str:
"""Fetch HTML via an external scraping API."""
async with aiohttp.ClientSession() as session:
async with session.get(…
Full Escalation (All Features Combined)
This example combines every layer: stealth mode, a list of proxies tried in order, retries, and a final fetch function.
What happens step by step:
| Round | Attempt | What runs | | --- | --- |…
import aiohttp
from crawl4ai import AsyncWebCrawler
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig, ProxyConfig
# Last-resort: fetch HTML via an external service
async def…
Tips
- Start with
max_retries=0and afallback_fetch_functionif you just want a safety net without burning time on retries. - Order proxies cheapest-first — datacenter proxies before residential,…
See Also
- Proxy & Security — Proxy setup, authentication, and rotation
- Undetected Browser — Stealth mode and browser fingerprint evasion
- [Session…
Crawl Dispatcher
This page announces the upcoming Crawl Dispatcher module in Crawl4AI, a feature for handling thousands of crawling tasks simultaneously with efficient resource management and real-time monitoring.
Crawl Dispatcher
We’re excited to announce a Crawl Dispatcher module that can handle thousands of crawling tasks simultaneously. By efficiently managing system resources (memory, CPU, network), this…
Download Handling in Crawl4AI
This guide explains how to use Crawl4AI to handle file downloads during crawling, including enabling downloads, specifying download locations, triggering downloads, and accessing downloaded files.
Overview
This guide explains how to use Crawl4AI to handle file downloads during crawling. You'll learn how to trigger downloads, specify download locations, and access downloaded files.
Enabling Downloads
To enable downloads, set the accept_downloads parameter in the BrowserConfig object and pass it to the crawler.
from crawl4ai.async_configs import BrowserConfig, AsyncWebCrawler
async def main():
config = BrowserConfig(accept_downloads=True) # Enable downloads globally
async with…
Specifying Download Location
Specify the download directory using the downloads_path attribute in the BrowserConfig object. If not provided, Crawl4AI defaults to creating a "downloads" directory inside the .crawl4ai folder…
from crawl4ai.async_configs import BrowserConfig
import os
downloads_path = os.path.join(os.getcwd(), "my_downloads") # Custom download path
os.makedirs(downloads_path, exist_ok=True)
config =…
Triggering Downloads
Downloads are typically triggered by user interactions on a web page, such as clicking a download button. Use js_code in CrawlerRunConfig to simulate these actions and wait_for to allow…
from crawl4ai.async_configs import CrawlerRunConfig
config = CrawlerRunConfig(
js_code="""
const downloadLink = document.querySelector('a[href$=".exe"]');
if (downloadLink) {…
Accessing Downloaded Files
The downloaded_files attribute of the CrawlResult object contains paths to downloaded files.
if result.downloaded_files:
print("Downloaded files:")
for file_path in result.downloaded_files:
print(f"- {file_path}")
file_size = os.path.getsize(file_path)…
Example: Downloading Multiple Files
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig
import os
from pathlib import Path
async def download_multiple_files(url: str, download_path: str):
config =…
Important Considerations
-
Browser Context: Downloads are managed within the browser context. Ensure
js_codecorrectly targets the download triggers on the webpage. -
Timing: Use
wait_forinCrawlerRunConfig…
Hooks & Auth in AsyncWebCrawler
This guide explains how to use hooks in AsyncWebCrawler to customize the crawling pipeline at specific stages, including authentication, route blocking, and pre/post-processing, with a detailed…
Introduction
Hooks & Auth in AsyncWebCrawler
Crawl4AI’s hooks let you customize the crawler at specific points in the pipeline:
1. on_browser_created – After browser creation.
2.…
Example: Using Hooks in AsyncWebCrawler
import asyncio
import json
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from playwright.async_api import Page, BrowserContext
async def main():
print("🔗 Hooks…
Hook Lifecycle Summary
1. on_browser_created :
-
Browser is up, but no pages or contexts yet.
-
Light setup only—don’t try to open or close pages here (that belongs in…
When to Handle Authentication
Recommended : Use on_page_context_created if you need to:
-
Navigate to a login page or fill forms
-
Set cookies or localStorage tokens
-
Block resource routes to avoid ads
This…
Additional Considerations
-
Session Management : If you want multiple
arun()calls to reuse a single session, passsession_id=in yourCrawlerRunConfig. Hooks remain the same. -
Performance : Hooks can slow…
Conclusion
Hooks provide fine-grained control over:
-
Browser creation (light tasks only)
-
Page and context creation (auth, route blocking)
-
Navigation phases
-
**Final…
Identity Based Crawling - Crawl4AI Documentation (v0.9.x)
This guide explains how to use Crawl4AI's Managed Browsers and BrowserProfiler to preserve a user's authentic digital identity while crawling, including persistent profiles, Magic Mode fallback, and…
Preserve Your Identity with Crawl4AI
Crawl4AI empowers you to navigate and interact with the web using your authentic digital identity, ensuring you’re recognized as a human and not mistaken for a bot. This tutorial covers:
1.…
1. Managed Browsers: Your Digital Identity Solution
Managed Browsers let developers create and use persistent browser profiles. These profiles store local storage, cookies, and other session data, letting you browse as your real self…
Key Benefits
- Authentic Browsing Experience: Retain session data and browser fingerprints as though you’re a normal user.
- Effortless Configuration: Once you log in or solve CAPTCHAs in your chosen data…
Creating a User Data Directory (Command-Line Approach via Playwright)
If you installed Crawl4AI (which installs Playwright under the hood), you already have a Playwright-managed Chromium on your system. Follow these steps to launch that Chromium from your command…
python -m playwright install --dry-run
playwright install --dry-run
~/.cache/ms-playwright/chromium-1234/chrome-linux/chrome
# Linux example
~/.cache/ms-playwright/chromium-1234/chrome-linux/chrome \
--user-data-dir=/home/<you>/my_chrome_profile
# macOS example (Playwright’s internal binary)
~/Library/Caches/ms-playwright/chromium-1234/chrome-mac/Chromium.app/Contents/MacOS/Chromium \
--user-data-dir=/Users/<you>/my_chrome_profile
# Windows example (PowerShell/cmd)
"C:\Users\<you>\AppData\Local\ms-playwright\chromium-1234\chrome-win\chrome.exe" ^
--user-data-dir="C:\Users\<you>\my_chrome_profile"
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
browser_config = BrowserConfig(
headless=True,
use_managed_browser=True,…
Creating a Profile Using the Crawl4AI CLI (Easiest)
If you prefer a guided, interactive setup, use the built-in CLI to create and manage persistent browser profiles.
-
Launch the profile manager (
crwl profiles). -
Choose "Create new profile" and…
crwl profiles
from crawl4ai import AsyncWebCrawler, BrowserConfig
profile_path = "/home/<you>/.crawl4ai/profiles/test_profile_1"
browser_config = BrowserConfig(
headless=True,
use_managed_browser=True,…
3. Using Managed Browsers in Crawl4AI
Once you have a data directory with your session data, pass it to BrowserConfig:
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
# 1) Reference your persistent data directory
browser_config = BrowserConfig(…
Workflow
- Login externally (via CLI or your normal Chrome with
--user-data-dir=...). - Close that browser.
- Use the same folder in
user_data_dir=in Crawl4AI. - Crawl – The site sees…
4. Magic Mode: Simplified Automation
If you don’t need a persistent profile or identity-based approach, Magic Mode offers a quick way to simulate human-like browsing without storing long-term data.
Magic Mode:
- Simulates…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://example.com",
config=CrawlerRunConfig(…
5. Comparing Managed Browsers vs. Magic Mode
| Feature | Managed Browsers | Magic Mode |
|---|---|---|
| Session Persistence | Full localStorage/cookies retained in user_data_dir | No persistent data (fresh each run) |
| … |
6. Using the BrowserProfiler Class
Crawl4AI provides a dedicated BrowserProfiler class for managing browser profiles, making it easy to create, list, and delete profiles for identity-based browsing.
Creating and Managing Profiles with BrowserProfiler
The BrowserProfiler class offers a comprehensive API for browser profile management:
How profile creation works:
- A browser window opens for you to interact with
- You log in to websites,…
import asyncio
from crawl4ai import BrowserProfiler
async def manage_profiles():
# Create a profiler instance
profiler = BrowserProfiler()
# Create a profile interactively - opens a…
Interactive Profile Management
The BrowserProfiler also offers an interactive management console that guides you through profile creation, listing, and deletion:
import asyncio
from crawl4ai import BrowserProfiler, AsyncWebCrawler, BrowserConfig
# Define a function to use a profile for crawling
async def crawl_with_profile(profile_path, url):…
Legacy Methods
For backward compatibility, the previous methods on ManagedBrowser are still available, but they delegate to the new BrowserProfiler class:
from crawl4ai.browser_manager import ManagedBrowser
# These methods still work but use BrowserProfiler internally
profiles = ManagedBrowser.list_profiles()
Complete Example
See the full example in docs/examples/identity_based_browsing.py for a complete demonstration of creating and using profiles for authenticated browsing using the new BrowserProfiler class.
7. Locale, Timezone, and Geolocation Control
In addition to using persistent profiles, Crawl4AI supports customizing your browser's locale, timezone, and geolocation settings. These features enhance your identity-based browsing experience by…
Setting Locale and Timezone
You can set the browser's locale and timezone through CrawlerRunConfig:
How it works:
localeaffects language preferences, date formats, number formats, etc.timezone_idaffects…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://example.com",
config=CrawlerRunConfig(…
Configuring Geolocation
Control the GPS coordinates reported by the browser's geolocation API:
Important notes:
- When
geolocationis specified, the browser is automatically granted permission to access location -…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, GeolocationConfig
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://maps.google.com", # Or any…
Combining with Managed Browsers
These settings work perfectly with managed browsers for a complete identity solution:
Combining persistent profiles with precise geolocation and region settings gives you complete control over your…
from crawl4ai import (
AsyncWebCrawler, BrowserConfig, CrawlerRunConfig,
GeolocationConfig
)
browser_config = BrowserConfig(
use_managed_browser=True,…
8. Summary
- Create your user-data directory either:
- By launching Chrome/Chromium externally with
--user-data-dir=/some/path - Or by using the built-in
BrowserProfiler.create_profile()method -…
- By launching Chrome/Chromium externally with
Lazy Loading - Crawl4AI Documentation (v0.9.x)
This guide explains how to handle lazy-loaded images in Crawl4AI by using wait_for_images, scan_full_page, and scroll_delay settings, and how to combine these with media filters and domain exclusions.
Handling Lazy-Loaded Images
Many websites now load images lazily as you scroll. If you need to ensure they appear in your final crawl (and in result.media), consider:
wait_for_images=True– Wait for images to…
Example: Ensuring Lazy Images Appear
Explanation:
-
wait_for_images=TrueThe crawler tries to ensure images have finished loading before finalizing the HTML.
-
scan_full_page=TrueTells the crawler to attempt…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, BrowserConfig
from crawl4ai.async_configs import CacheMode
async def main():
config = CrawlerRunConfig(
# Force the…
Combining with Other Link & Media Filters
You can still combine lazy-load logic with the usual exclude_external_images, exclude_domains, or link filtration:
This approach ensures you see all images from the main domain while…
config = CrawlerRunConfig(
wait_for_images=True,
scan_full_page=True,
scroll_delay=0.5,
# Filter out external images if you only want local ones
exclude_external_images=True,…
Tips & Troubleshooting
-
Long Pages
-
Setting
scan_full_page=Trueon extremely long or infinite-scroll pages can be resource-intensive. -
Consider using hooks or specialized…
-
Advanced Multi-URL Crawling with Dispatchers
This page covers advanced multi-URL crawling using dispatchers in Crawl4AI, including RateLimiter, CrawlerMonitor, MemoryAdaptiveDispatcher, and SemaphoreDispatcher, with usage examples and…
1. Introduction
Heads Up : Crawl4AI supports advanced dispatchers for parallel or throttled crawling, providing dynamic rate limiting and memory usage checks. The built-in
arun_many()function uses…
2. Core Components
2.1 Rate Limiter
Here’s the revised and simplified explanation of the RateLimiter , focusing on constructor parameters and adhering to your markdown style and mkDocs guidelines.
class RateLimiter:
def __init__(
# Random delay range between requests
base_delay: Tuple[float, float] = (1.0, 3.0),
# Maximum backoff delay
max_delay: float =…
RateLimiter Constructor Parameters
The RateLimiter is a utility that helps manage the pace of requests to avoid overloading servers or getting blocked due to rate limits. It operates internally to delay requests and handle retries…
from crawl4ai import RateLimiter
# Create a RateLimiter with custom settings
rate_limiter = RateLimiter(
base_delay=(2.0, 4.0), # Random delay between 2-4 seconds
max_delay=30.0, #…
2.2 Crawler Monitor
The CrawlerMonitor provides real-time visibility into crawling operations:
Display Modes :
- DETAILED : Shows individual task status, memory usage, and timing
- AGGREGATED : Displays…
from crawl4ai import CrawlerMonitor, DisplayMode
monitor = CrawlerMonitor(
# Maximum rows in live display
max_visible_rows=15,
# DETAILED or AGGREGATED view…
3. Available Dispatchers
3.1 MemoryAdaptiveDispatcher (Default)
Automatically manages concurrency based on system memory usage:
Constructor Parameters:
-
memory_threshold_percent(float, default:90.0)Specifies the memory usage threshold…
from crawl4ai.async_dispatcher import MemoryAdaptiveDispatcher
dispatcher = MemoryAdaptiveDispatcher(
memory_threshold_percent=90.0, # Pause if memory exceeds this
check_interval=1.0,…
3.2 SemaphoreDispatcher
Provides simple concurrency control with a fixed limit:
Constructor Parameters:
-
max_session_permit(int, default:20)The maximum number of concurrent crawling tasks allowed,…
from crawl4ai.async_dispatcher import SemaphoreDispatcher
dispatcher = SemaphoreDispatcher(
max_session_permit=20, # Maximum concurrent tasks
rate_limiter=RateLimiter( #…
4. Usage Examples
4.1 Batch Processing (Default)
Review:
- Purpose: Executes a batch crawl with all URLs processed together after crawling is complete.
- Dispatcher: Uses
MemoryAdaptiveDispatcherto manage concurrency and system…
async def crawl_batch():
browser_config = BrowserConfig(headless=True, verbose=False)
run_config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
stream=False # Default: get…
4.2 Streaming Mode
Review:
- Purpose: Enables streaming to process results as soon as they’re available.
- Dispatcher: Uses
MemoryAdaptiveDispatcherfor concurrency and memory management. - Stream:…
async def crawl_streaming():
browser_config = BrowserConfig(headless=True, verbose=False)
run_config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
stream=True # Enable…
4.3 Semaphore-based Crawling
Review:
- Purpose: Uses
SemaphoreDispatcherto limit concurrency with a fixed number of slots. - Dispatcher: Configured with a semaphore to control parallel crawling tasks.
- **Rate…
async def crawl_with_semaphore(urls):
browser_config = BrowserConfig(headless=True, verbose=False)
run_config = CrawlerRunConfig(cache_mode=CacheMode.BYPASS)
dispatcher =…
4.4 Robots.txt Consideration
Review:
- Purpose: Ensures compliance with
robots.txtrules for ethical and legal web crawling. - Configuration: Set
check_robots_txt=Trueto validate each URL againstrobots.txt…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
urls = [
"https://example1.com",
"https://example2.com",…
5. Dispatch Results
Each crawl result includes dispatch information:
Access via result.dispatch_result:
@dataclass
class DispatchResult:
task_id: str
memory_usage: float
peak_memory: float
start_time: datetime
end_time: datetime
error_message: str = ""
Copy
for result in results:
if result.success:
dr = result.dispatch_result
print(f"URL: {result.url}")
print(f"Memory: {dr.memory_usage:.1f}MB")
print(f"Duration:…
6. URL-Specific Configurations
When crawling diverse content types, you often need different configurations for different URLs. For example:
- PDFs need specialized extraction
- Blog pages benefit from content filtering
- Dynamic…
6.1 Basic URL Pattern Matching
Important : A CrawlerRunConfig without url_matcher (or with url_matcher=None) matches ALL URLs. This makes it perfect as a default/fallback configuration.
The url_matcher parameter…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, MatchMode
from crawl4ai.processors.pdf import PDFContentScrapingStrategy
from crawl4ai.extraction_strategy import…
6.2 Advanced Pattern Matching
Important : A CrawlerRunConfig without url_matcher (or with url_matcher=None) matches ALL URLs. This makes it perfect as a default/fallback configuration.
The url_matcher parameter…
Glob Patterns (Strings)
# Simple patterns
"*.pdf" # Any PDF file
"*/api/*" # Any URL with /api/ in path
"https://*.example.com/*" # Subdomain matching
"*://example.com/blog/*" # Any…
Custom Functions
# Complex logic with lambdas
lambda url: url.startswith('https://') and 'secure' in url
lambda url: len(url) > 50 and url.count('/') > 5
lambda url: any(domain in url for domain in ['api.', 'data.',…
Mixed Lists with AND/OR Logic
# Combine multiple conditions
CrawlerRunConfig(
url_matcher=[
"https://*", # Must be HTTPS
lambda url: 'internal' in url, # Must contain 'internal'…
6.3 Practical Example: News Site Crawler
async def crawl_news_site():
dispatcher = MemoryAdaptiveDispatcher(
memory_threshold_percent=70.0,
rate_limiter=RateLimiter(base_delay=(1.0, 2.0))
)
configs = [
#…
6.4 Best Practices
- Order Matters : Configs are evaluated in order - put specific patterns before general ones
- Default Config Behavior :
- A config without
url_matchermatches ALL URLs - Always include…
- A config without
config = CrawlerRunConfig(url_matcher="*.pdf")
print(config.is_match("https://example.com/doc.pdf")) # True
default_config = CrawlerRunConfig() # No…
7. Summary
-
Two Dispatcher Types :
- MemoryAdaptiveDispatcher (default): Dynamic concurrency based on memory
- SemaphoreDispatcher: Fixed concurrency limit
-
Optional Components :
-…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| base_delay | Tuple[float, float] | The range for a random delay (in seconds) between consecutive requests to the same domain. A random delay is chosen between base_delay[0] and base_delay[1] for each request. | (1.0, 3.0) | No |
| max_delay | float | The maximum allowable delay when rate-limiting errors occur. When servers return rate-limit responses (e.g., 429 or 503), the delay increases exponentially with jitter, capped at this value. | 60.0 | No |
| max_retries | int | The maximum number of retries for a request if rate-limiting errors occur. After encountering a rate-limit response, the RateLimiter retries the request up to this number of times. | 3 | No |
| rate_limit_codes | List[int] | A list of HTTP status codes that trigger the rate-limiting logic. These status codes indicate the server is overwhelmed or actively limiting requests. | [429, 503] | No |
| memory_threshold_percent | float | Specifies the memory usage threshold (as a percentage). If system memory usage exceeds this value, the dispatcher pauses crawling to prevent system overload. | 90.0 | No |
| check_interval | float | The interval (in seconds) at which the dispatcher checks system memory usage. | 1.0 | No |
| max_session_permit | int | The maximum number of concurrent crawling tasks allowed. This ensures resource limits are respected while maintaining concurrency. | 10 | No |
| memory_wait_timeout | float | Optional timeout (in seconds). If memory usage exceeds memory_threshold_percent for longer than this duration, a MemoryError is raised. | 600.0 | No |
| rate_limiter | RateLimiter | Optional rate-limiting logic to avoid server-side blocking (e.g., for handling 429 or 503 errors). | None | No |
| monitor | CrawlerMonitor | Optional monitoring for real-time task tracking and performance insights. | None | No |
| max_session_permit | int | The maximum number of concurrent crawling tasks allowed, irrespective of semaphore slots. | 20 | No |
| rate_limiter | RateLimiter | Optional rate-limiting logic to avoid overwhelming servers. | None | No |
| monitor | CrawlerMonitor | Optional monitoring for tracking task progress and resource usage. | None | No |
| max_visible_rows | int | Maximum rows in live display. | 15 | No |
| display_mode | DisplayMode | DETAILED or AGGREGATED view. | DisplayMode.DETAILED | No |
Network Requests & Console Message Capturing
This page explains how to capture network requests and browser console messages during a crawl using Crawl4AI, including configuration, example usage, data structures, benefits, and use cases.
Configuration
To enable network and console capturing, use these configuration options:
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
# Enable both network request capture and console message capture
config = CrawlerRunConfig(
capture_network_requests=True, # Capture all…
Example Usage
import asyncio
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def main():
# Enable both network request capture and console message capture
config =…
Captured Data Structure
The result.network_requests contains a list of dictionaries, each representing a network event with these common fields:
| Field | Description |
|---|---|
event_type |
Type of event:… |
Network Requests
The result.network_requests contains a list of dictionaries, each representing a network event with these common fields:
| Field | Description |
|---|---|
event_type |
Type of event:… |
Request Event Fields
{
"event_type": "request",
"url": "https://example.com/api/data.json",
"method": "GET",
"headers": {"User-Agent": "...", "Accept": "..."},
"post_data": "key=value&otherkey=value",…
Response Event Fields
{
"event_type": "response",
"url": "https://example.com/api/data.json",
"status": 200,
"status_text": "OK",
"headers": {"Content-Type": "application/json", "Cache-Control": "..."},…
Failed Request Event Fields
{
"event_type": "request_failed",
"url": "https://example.com/missing.png",
"method": "GET",
"resource_type": "image",
"failure_text": "net::ERR_ABORTED 404",
"timestamp": 1633456789.789
}
Console Messages
The result.console_messages contains a list of dictionaries, each representing a console message with these common fields:
| Field | Description |
|---|---|
type |
Message type: "log",… |
Console Message Example
{
"type": "error",
"text": "Uncaught TypeError: Cannot read property 'length' of undefined",
"location": "https://example.com/script.js:123:45",
"timestamp": 1633456790.123
}
Key Benefits
Full Request Visibility : Capture all network activity including:
- Requests (URLs, methods, headers, post data)
- Responses (status codes, headers, timing)
- Failed requests (with error…
Use Cases
API Discovery : Identify hidden endpoints and data flows in single-page applications
Debugging : Track down JavaScript errors affecting page functionality
Security Auditing : Detect…
PDF Processing Strategies
This page describes the PDF processing strategies in Crawl4AI, including PDFCrawlerStrategy and PDFContentScrapingStrategy, which enable crawling and extracting content from PDF files.
Overview
PDFCrawlerStrategy is an implementation of AsyncCrawlerStrategy designed specifically for PDF documents. Instead of interpreting the input URL as an HTML webpage, this strategy treats it as a…
When to Use
Use PDFCrawlerStrategy when you need to:
- Process PDF files using the
AsyncWebCrawler. - Handle PDFs from both web URLs (e.g.,
https://example.com/document.pdf) and local file paths (e.g.,…
Key Methods and Their Behavior
-
__init__(self, logger: AsyncLogger = None): -
Initializes the strategy.
-
logger: An optionalAsyncLoggerinstance (fromcrawl4ai.async_logger) for logging purposes. -
**`async…
Example Usage
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.processors.pdf import PDFCrawlerStrategy, PDFContentScrapingStrategy
async def main():
# Initialize the PDF…
Pros and Cons
Pros:
- Enables
AsyncWebCrawlerto handle PDF sources directly using familiararuncalls. - Provides a consistent interface for specifying PDF sources (URLs or local paths). -…
Key Configuration Attributes
When initializing PDFContentScrapingStrategy, you can configure its behavior using the following attributes:
extract_images: bool = False: IfTrue, the strategy will attempt to…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| logger | AsyncLogger | An optional AsyncLogger instance for logging purposes. | None | No |
| url | str | The URL pointing to a PDF file. | Yes | |
| save_images_locally | bool | If True (and extract_images is also True), extracted images will be saved to disk in the image_save_dir. | False | No |
| extract_images | bool | If True, the strategy will attempt to extract images from the PDF. | False | No |
| image_save_dir | str | Specifies the directory where extracted images should be saved if save_images_locally is True. | None | No |
| batch_size | int | Defines how many PDF pages are processed in a single batch. | 4 | No |
| logger | AsyncLogger | An optional AsyncLogger instance for logging. | None | No |
| url | str | The path or URL to the PDF file. | Yes | |
| html | str | Typically an empty string when used with PDFCrawlerStrategy, as the content is a PDF, not HTML. | Yes | |
| url | str | The path or URL to the PDF file. | Yes | |
| html | str | Typically an empty string when used with PDFCrawlerStrategy, as the content is a PDF, not HTML. | Yes | |
| url | str | The URL or path to the PDF file. | Yes |
Proxy & Security
This guide covers proxy configuration and security features in Crawl4AI, including SSL certificate analysis and proxy rotation strategies.
Understanding Proxy Configuration
Crawl4AI recommends configuring proxies per request through CrawlerRunConfig.proxy_config. This gives you precise control, enables rotation strategies, and keeps examples simple enough to copy,…
Basic Proxy Setup
Configure proxies that apply to each crawl operation:
Why request-level?
CrawlerRunConfig.proxy_config keeps each request self-contained, so swapping proxies or rotation strategies is just a…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, ProxyConfig
run_config = CrawlerRunConfig(proxy_config=ProxyConfig(server="http://proxy.example.com:8080"))
#…
Supported Proxy Formats
The ProxyConfig.from_string() method supports multiple formats:
from crawl4ai import ProxyConfig
# HTTP proxy with authentication
proxy1 = ProxyConfig.from_string("http://user:pass@192.168.1.1:8080")
# HTTPS proxy
proxy2 =…
Authenticated Proxies
For proxies requiring authentication:
import asyncio
from crawl4ai import AsyncWebCrawler,BrowserConfig, CrawlerRunConfig, ProxyConfig
run_config = CrawlerRunConfig(
proxy_config=ProxyConfig(…
Environment Variable Configuration
Load proxies from environment variables for easy configuration:
import os
from crawl4ai import ProxyConfig, CrawlerRunConfig
# Set environment variable
os.environ["PROXIES"] = "ip1:port1:user1:pass1,ip2:port2:user2:pass2,ip3:port3"
# Load all proxies
proxies =…
Rotating Proxies
Crawl4AI supports automatic proxy rotation to distribute requests across multiple proxy servers. Rotation is applied per request using a rotation strategy on CrawlerRunConfig.
Proxy Rotation (recommended)
import asyncio
import re
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode, ProxyConfig
from crawl4ai.proxy_strategy import RoundRobinProxyStrategy
async def main():…
SSL Certificate Analysis
Combine proxy usage with SSL certificate inspection for enhanced security analysis. SSL certificate fetching is configured per request via CrawlerRunConfig.
Per-Request SSL Certificate Analysis
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
run_config = CrawlerRunConfig(
proxy_config={
"server": "http://proxy.example.com:8080",…
Security Best Practices
1. Proxy Rotation for Anonymity
from crawl4ai import CrawlerRunConfig, ProxyConfig
from crawl4ai.proxy_strategy import RoundRobinProxyStrategy
# Use multiple proxies to avoid IP blocking
proxies =…
2. SSL Certificate Verification
from crawl4ai import CrawlerRunConfig
# Always verify SSL certificates when possible
# Per-request (affects specific requests)
run_config = CrawlerRunConfig(fetch_ssl_certificate=True)
3. Environment Variable Security
# Use environment variables for sensitive proxy credentials
# Avoid hardcoding usernames/passwords in code
export PROXIES="ip1:port1:user1:pass1,ip2:port2:user2:pass2"
4. SOCKS5 for Enhanced Security
from crawl4ai import CrawlerRunConfig
# Prefer SOCKS5 proxies for better protocol support
run_config = CrawlerRunConfig(proxy_config="socks5://proxy.example.com:1080")
Migration from Deprecated `proxy` Parameter
The legacy proxy argument on BrowserConfig is deprecated. Configure proxies through CrawlerRunConfig.proxy_config so each request fully describes its network settings.
# Old (deprecated) approach
# from crawl4ai import BrowserConfig
# browser_config = BrowserConfig(proxy_config="http://proxy.example.com:8080")
# New (preferred) approach
from crawl4ai import…
Safe Logging of Proxies
from crawl4ai import ProxyConfig
def safe_proxy_repr(proxy: ProxyConfig):
if getattr(proxy, "username", None):
return f"{proxy.server} (auth: ****)"
return proxy.server
Troubleshooting
Common Issues
Proxy connection failed
- Verify the proxy server is reachable from your network.
- Double-check authentication credentials.
- Ensure the protocol matches (
http,https, orsocks5).
SSL…
See Also
Anti-Bot Detection & Fallback — Automatic retry with proxy escalation and fallback functions when anti-bot blocking is detected
Session Management - Crawl4AI Documentation (v0.9.x)
This page explains how to use session management in Crawl4AI to maintain state across multiple requests, enabling sequential crawling, dynamic content handling, and advanced techniques like custom…
Overview
Session management in Crawl4AI is a powerful feature that allows you to maintain state across multiple requests, making it particularly suitable for handling complex multi-step crawling tasks. It…
Basic Session Usage
Use BrowserConfig and CrawlerRunConfig to maintain state with a session_id:
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig
async with AsyncWebCrawler() as crawler:
session_id = "my_session"
# Define configurations
config1 =…
Dynamic Content with Sessions
Here's an example of crawling GitHub commits across multiple pages while preserving session state:
from crawl4ai.async_configs import CrawlerRunConfig
from crawl4ai import JsonCssExtractionStrategy
from crawl4ai.cache_context import CacheMode
async def crawl_dynamic_content():
url =…
Example 1: Basic Session-Based Crawling
A simple example using session-based crawling:
This example shows:
- Reusing the same
session_idacross multiple requests. - Executing JavaScript to load more content dynamically.
- Properly…
import asyncio
from crawl4ai.async_configs import BrowserConfig, CrawlerRunConfig
from crawl4ai.cache_context import CacheMode
async def basic_session_crawl():
async with AsyncWebCrawler() as…
Advanced Technique 1: Custom Execution Hooks
Warning: You might feel confused by the end of the next few examples 😅, so make sure you are comfortable with the order of the parts before you start this.
Use custom hooks to handle complex…
async def advanced_session_crawl_with_hooks():
first_commit = ""
async def on_execution_started(page):
nonlocal first_commit
try:
while True:…
Advanced Technique 2: Integrated JavaScript Execution and Waiting
Combine JavaScript execution and waiting logic for concise handling of dynamic content:
async def integrated_js_and_wait_crawl():
async with AsyncWebCrawler() as crawler:
session_id = "integrated_session"
url = "https://github.com/example/repo/commits/main"…
Common Use Cases for Sessions
-
Authentication Flows: Login and interact with secured pages.
-
Pagination Handling: Navigate through multiple pages.
-
Form Submissions: Fill forms, submit, and process…
SSLCertificate Reference
Reference for the SSLCertificate class in Crawl4AI, covering how to load, inspect, and export SSL/TLS certificate data, and how to use it with fetch_ssl_certificate=True in CrawlerRunConfig.
1. Overview
The SSLCertificate class encapsulates an SSL certificate’s data and allows exporting it in various formats (PEM, DER, JSON, or text). It’s used within Crawl4AI whenever you set…
class SSLCertificate:
"""
Represents an SSL certificate with methods to export in various formats.
Main Methods:
- from_url(url, timeout=10)
- from_file(file_path)
-…
Typical Use Case
- You enable certificate fetching in your crawl by:
- After
arun(), ifresult.ssl_certificateis present, it’s an instance ofSSLCertificate. - You can read basic properties…
CrawlerRunConfig(fetch_ssl_certificate=True, ...)
2. Construction & Fetching
2.1 from_url(url, timeout=10)
Manually load an SSL certificate from a given URL (port 443). Typically used internally, but you can call it directly if you want:
cert = SSLCertificate.from_url("https://example.com")
if cert:
print("Fingerprint:", cert.fingerprint)
2.2 from_file(file_path)
Load from a file containing certificate data in ASN.1 or DER. Rarely needed unless you have local cert files:
cert = SSLCertificate.from_file("/path/to/cert.der")
2.3 from_binary(binary_data)
Initialize from raw binary. E.g., if you captured it from a socket or another source:
cert = SSLCertificate.from_binary(raw_bytes)
3. Common Properties
After obtaining a SSLCertificate instance (e.g. result.ssl_certificate from a crawl), you can read:
issuer(dict)- E.g.
{"CN": "My Root CA", "O": "..."}
- E.g.
subject…
4. Export Methods
Once you have a SSLCertificate object, you can export or inspect it:
4.1 to_json(filepath=None) → Optional[str]
- Returns a JSON string containing the parsed certificate fields.
- If
filepathis provided, saves it to disk instead, returningNone.
Usage:
json_data = cert.to_json() # returns JSON string
cert.to_json("certificate.json") # writes file, returns None
4.2 to_pem(filepath=None) → Optional[str]
- Returns a PEM-encoded string (common for web servers).
- If
filepathis provided, saves it to disk instead.
pem_str = cert.to_pem() # in-memory PEM string
cert.to_pem("/path/to/cert.pem") # saved to file
4.3 to_der(filepath=None) → Optional[bytes]
- Returns the original DER (binary ASN.1) bytes.
- If
filepathis specified, writes the bytes there instead.
der_bytes = cert.to_der()
cert.to_der("certificate.der")
4.4 (Optional) export_as_text()
- If you see a method like
export_as_text(), it typically returns an OpenSSL-style textual representation. - Not always needed, but can help for debugging or manual inspection.
5. Example Usage in Crawl4AI
Below is a minimal sample showing how the crawler obtains an SSL cert from a site, then reads or exports it. The code snippet:
import asyncio
import os
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
tmp_dir = "tmp"
os.makedirs(tmp_dir, exist_ok=True)
config =…
6. Notes & Best Practices
- Timeout:
SSLCertificate.from_urlinternally uses a default 10s socket connect and wraps SSL. - Binary Form: The certificate is loaded in ASN.1 (DER) form, then re-parsed by…
Summary
SSLCertificateis a convenience class for capturing and exporting the TLS certificate from your crawled site(s).- Common usage is in the
CrawlResult.ssl_certificatefield,…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| from_url.url | str | The URL to load the SSL certificate from (port 443). | Yes | |
| from_url.timeout | int | Socket connect timeout in seconds. | 10 | No |
| from_file.file_path | str | Path to a file containing certificate data in ASN.1 or DER. | Yes | |
| from_binary.binary_data | bytes | Raw binary certificate data. | Yes | |
| to_json.filepath | Optional[str] | If provided, saves the JSON output to disk instead of returning a string. | None | No |
| to_pem.filepath | Optional[str] | If provided, saves the PEM output to disk instead of returning a string. | None | No |
| to_der.filepath | Optional[str] | If provided, writes the DER bytes to disk instead of returning bytes. | None | No |
Undetected Browser Mode
This guide covers Crawl4AI's anti-bot features: Stealth Mode and Undetected Browser Mode, including how to use them, when to use each, and best practices for evading bot detection.
Overview
Crawl4AI offers two powerful anti-bot features to help you access websites with bot detection:
- Stealth Mode - Uses playwright-stealth to modify browser fingerprints and behaviors -…
Anti-Bot Features Comparison
| Feature | Regular Browser | Stealth Mode | Undetected Browser |
|---|---|---|---|
| WebDriver Detection | ❌ | ✅ | ✅ |
| Navigator Properties | ❌ | ✅ | ✅ |
| Plugin Emulation | ❌ | ✅ | ✅ |
| … |
When to Use Each Approach
Use Regular Browser + Stealth Mode When:
- Sites have basic bot detection (checking navigator.webdriver, plugins, etc.)
- You need good performance with basic protection
- Sites check for common automation indicators
Use Undetected Browser When:
- Sites employ sophisticated bot detection services (Cloudflare, DataDome, etc.)
- Stealth mode alone isn't sufficient
- You're willing to trade some performance for better evasion
Best Practice: Progressive Enhancement
- Start with: Regular browser + Stealth mode
- If blocked: Switch to Undetected browser
- If still blocked: Combine Undetected browser + Stealth mode
Stealth Mode
Stealth mode is the simpler anti-bot solution that works with both regular and undetected browsers:
from crawl4ai import AsyncWebCrawler, BrowserConfig
# Enable stealth mode with regular browser
browser_config = BrowserConfig(
enable_stealth=True, # Simple flag to enable
headless=False…
What Stealth Mode Does:
- Removes
navigator.webdriverflag - Modifies browser fingerprints
- Emulates realistic plugin behavior
- Adjusts navigator properties
- Fixes common automation leaks
Undetected Browser Mode
For sites with sophisticated bot detection that stealth mode can't bypass, use the undetected browser adapter:
Key Features
- Drop-in Replacement: Uses the same API as regular browser mode
- Enhanced Stealth: Built-in patches to evade common detection methods
- Browser Adapter Pattern: Seamlessly switch…
Quick Start
import asyncio
from crawl4ai import (
AsyncWebCrawler,
BrowserConfig,
CrawlerRunConfig,
UndetectedAdapter
)
from crawl4ai.async_crawler_strategy import…
Combining Both Features
For maximum evasion, combine stealth mode with undetected browser:
from crawl4ai import AsyncWebCrawler, BrowserConfig, UndetectedAdapter
from crawl4ai.async_crawler_strategy import AsyncPlaywrightCrawlerStrategy
# Create browser config with stealth…
Examples
Example 1: Basic Stealth Mode
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def test_stealth_mode():
# Simple stealth mode configuration
browser_config = BrowserConfig(…
Example 2: Undetected Browser Mode
import asyncio
from crawl4ai import (
AsyncWebCrawler,
BrowserConfig,
CrawlerRunConfig,
UndetectedAdapter
)
from crawl4ai.async_crawler_strategy import…
Browser Adapter Pattern
The undetected browser support is implemented using an adapter pattern, allowing seamless switching between different browser implementations:
The adapter handles:
- JavaScript execution
- Console…
# Regular browser adapter (default)
from crawl4ai import PlaywrightAdapter
regular_adapter = PlaywrightAdapter()
# Undetected browser adapter
from crawl4ai import…
Best Practices
- Avoid Headless Mode: Detection is easier in headless mode
- Use Reasonable Delays: Don't rush through pages
- Rotate User Agents: You can customize user agents
- **Handle Failures…
browser_config = BrowserConfig(headless=False)
crawler_config = CrawlerRunConfig(
wait_time=3.0, # Wait 3 seconds after page load
delay_before_return_html=2.0 # Additional delay
)
browser_config = BrowserConfig(
headers={"User-Agent": "your-user-agent"}
)
if not result.success:
print(f"Crawl failed: {result.error_message}")
Advanced Usage Tips
Progressive Detection Handling
async def crawl_with_progressive_evasion(url):
# Step 1: Try regular browser with stealth
browser_config = BrowserConfig(
enable_stealth=True,
headless=False
)
async…
Installation
The undetected browser dependencies are automatically installed when you run:
This command installs all necessary browser dependencies for both regular and undetected modes.
crawl4ai-setup
Limitations
- Performance: Slightly slower than regular mode due to additional patches
- Headless Detection: Some sites can still detect headless mode
- Resource Usage: May use more resources than…
Troubleshooting
Browser Not Found
Run the setup command:
crawl4ai-setup
Detection Still Occurring
Try combining with other features:
crawler_config = CrawlerRunConfig(
simulate_user=True, # Add user simulation
magic=True, # Enable magic mode
wait_time=5.0, # Longer waits
)
Performance Issues
If experiencing slow performance:
# Use selective undetected mode only for protected sites
if is_protected_site(url):
adapter = UndetectedAdapter()
else:
adapter = PlaywrightAdapter() # Default adapter
Future Plans
Note: In future versions of Crawl4AI, we may enable stealth mode and undetected browser by default to provide better out-of-the-box success rates. For now, users should explicitly enable these…
Conclusion
Crawl4AI provides flexible anti-bot solutions:
- Start Simple: Use regular browser + stealth mode for most sites
- Escalate if Needed: Switch to undetected browser for sophisticated…
See Also
- Advanced Features - Overview of all advanced features
- Proxy & Security - Using proxies with anti-bot features
- [Session…
Virtual Scroll - Crawl4AI Documentation (v0.9.x)
This page explains Crawl4AI's Virtual Scroll feature for handling virtual scrolling websites, covering configuration, usage examples, comparison with scan_full_page, and performance tips.
Understanding Virtual Scroll
Modern websites increasingly use virtual scrolling (also called windowed rendering or viewport rendering) to handle large datasets efficiently. This technique only renders visible items in the DOM,…
Traditional Scroll: Virtual Scroll:
┌─────────────┐ ┌─────────────┐
│ Item 1 │ │ Item 11 │ <- Items 1-10 removed
│ Item 2 │ │ Item 12 │…
Basic Usage
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, VirtualScrollConfig
# Configure virtual scroll
virtual_config = VirtualScrollConfig(
container_selector="#feed", # CSS selector for…
Configuration Parameters
VirtualScrollConfig
| Parameter | Type | Default | Description |
|---|---|---|---|
container_selector |
str |
Required | CSS selector for the scrollable container |
| … |
Real-World Examples
Twitter-like Timeline
Twitter replaces tweets as you scroll.
Instagram Grid
Instagram uses virtualized grid for performance.
Mixed Content (News Feed)
Some sites mix static and…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, VirtualScrollConfig, BrowserConfig
async def crawl_twitter_timeline():
# Twitter replaces tweets as you scroll
virtual_config =…
async def crawl_instagram_grid():
# Instagram uses virtualized grid for performance
virtual_config = VirtualScrollConfig(
container_selector="article", # Main feed container…
async def crawl_mixed_feed():
# Featured articles stay, regular articles virtualize
virtual_config = VirtualScrollConfig(
container_selector=".main-feed",
scroll_count=25,…
Virtual Scroll vs scan_full_page
Both features handle dynamic content, but serve different purposes:
| Feature | Virtual Scroll | scan_full_page |
|---|---|---|
| Purpose | Capture content that's replaced during scroll | … |
Combining with Extraction
Virtual Scroll works seamlessly with extraction strategies.
from crawl4ai import LLMExtractionStrategy, LLMConfig
# Define extraction schema
schema = {
"type": "array",
"items": {
"type": "object",
"properties": {…
Performance Tips
- Container Selection: Be specific with selectors. Using the correct container improves performance.
- Scroll Count: Start conservative and increase as needed.
- Wait Times: Adjust based…
# Start with fewer scrolls
virtual_config = VirtualScrollConfig(
container_selector="#feed",
scroll_count=10 # Test with 10, increase if needed
)
# Fast sites
wait_after_scroll=0.2
# Slower sites or heavy content
wait_after_scroll=1.5
browser_config = BrowserConfig(headless=False)
async with AsyncWebCrawler(config=browser_config) as crawler:
# Watch the scrolling happen
How It Works Internally
- Detection Phase: Scrolls and compares HTML to detect behavior
- Capture Phase: For replaced content, stores HTML chunks at each position
- Merge Phase: Combines all chunks, removing…
Error Handling
Virtual Scroll handles errors gracefully. If the container isn't found, crawling continues normally without virtual scroll.
# If container not found or scrolling fails
result = await crawler.arun(url="...", config=config)
if result.success:
# Virtual scroll worked or wasn't needed
print(f"Captured…
Complete Example
See our comprehensive example that demonstrates:
- Twitter-like feeds
- Instagram grids
- Traditional infinite scroll
- Mixed content scenarios
- Performance comparisons
The example includes a local…
# Run the examples
cd docs/examples
python virtual_scroll_example.py
API Reference
AdaptiveCrawler
The AdaptiveCrawler class implements intelligent web crawling that automatically determines when sufficient information has been gathered to answer a query. It uses a three-layer scoring system to…
Constructor
Parameters
- crawler (
AsyncWebCrawler): The underlying web crawler instance to use for fetching pages - config (
Optional[AdaptiveConfig]): Configuration settings for adaptive…
AdaptiveCrawler(
crawler: AsyncWebCrawler,
config: Optional[AdaptiveConfig] = None
)
Primary Method
digest()
The main method that performs adaptive crawling starting from a URL with a specific query.
Parameters
- start_url (
str): The starting URL for crawling - query…
async def digest(
start_url: str,
query: str,
resume_from: Optional[Union[str, Path]] = None
) -> CrawlState
async with AsyncWebCrawler() as crawler:
adaptive = AdaptiveCrawler(crawler)
state = await adaptive.digest(
start_url="https://docs.python.org",
query="async context…
Properties
confidence
Current confidence score (0-1) indicating information sufficiency.
coverage_stats
Dictionary containing detailed coverage statistics.
Returns:
- coverage : Query term…
@property
def confidence(self) -> float
@property
def coverage_stats(self) -> Dict[str, float]
@property
def is_sufficient(self) -> bool
@property
def state(self) -> CrawlState
Methods
get_relevant_content()
Retrieve the most relevant content from the knowledge base.
Parameters
- top_k (
int): Number of top relevant documents to return (default: 5)
####…
def get_relevant_content(
self,
top_k: int = 5
) -> List[Dict[str, Any]]
def print_stats(
self,
detailed: bool = False
) -> None
def export_knowledge_base(
self,
path: Union[str, Path]
) -> None
adaptive.export_knowledge_base("my_knowledge.jsonl")
async def import_knowledge_base(
self,
path: Union[str, Path]
) -> None
Configuration
The AdaptiveConfig class controls the behavior of adaptive crawling:
Example with Custom Config
@dataclass
class AdaptiveConfig:
confidence_threshold: float = 0.8 # Stop when confidence reaches this
max_pages: int = 50 # Maximum pages to crawl
top_k_links:…
config = AdaptiveConfig(
confidence_threshold=0.7,
max_pages=20,
top_k_links=3
)
adaptive = AdaptiveCrawler(crawler, config=config)
Complete Example
import asyncio
from crawl4ai import AsyncWebCrawler, AdaptiveCrawler, AdaptiveConfig
async def main():
# Configure adaptive crawling
config = AdaptiveConfig(…
See Also
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| crawler | AsyncWebCrawler | The underlying web crawler instance to use for fetching pages | Yes | |
| config | Optional[AdaptiveConfig] | Configuration settings for adaptive crawling behavior. If not provided, uses default settings. | None | No |
| start_url | str | The starting URL for crawling | Yes | |
| query | str | The search query that guides the crawling process | Yes | |
| resume_from | Optional[Union[str, Path]] | Path to a saved state file to resume from | None | No |
| top_k | int | Number of top relevant documents to return | 5 | No |
| detailed | bool | If True, shows detailed metrics with colors. If False, shows summary table. | False | No |
| path | Union[str, Path] | Output file path for JSONL export | Yes | |
| path | Union[str, Path] | Path to JSONL file to import | Yes |
`arun()` Parameter Guide (New Approach)
A guide to the parameters of the `arun()` method in Crawl4AI, now organized under `CrawlerRunConfig`, covering caching, content processing, navigation, session management, media options, extraction,…
Introduction
In Crawl4AI's latest configuration model, nearly all parameters that once went directly to arun() are now part of CrawlerRunConfig . When calling arun(), you provide:
Below is an…
await crawler.arun(
url="https://example.com",
config=my_run_config
)
1. Core Usage
Key Fields:
verbose=Truelogs each crawl step.cache_modedecides how to read/write the local crawl cache.
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
run_config = CrawlerRunConfig(
verbose=True, # Detailed logging…
2. Cache Control
cache_mode (default: CacheMode.ENABLED)
Use a built-in enum from CacheMode:
ENABLED: Normal caching—reads if available, writes if missing.DISABLED: No caching—always refetch…
run_config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS
)
3.1 Text Processing
run_config = CrawlerRunConfig(
word_count_threshold=10, # Ignore text blocks <10 words
only_text=False, # If True, tries to remove non-text elements
keep_data_attributes=False…
3.2 Content Selection
run_config = CrawlerRunConfig(
css_selector=".main-content", # Focus on .main-content region only
excluded_tags=["form", "nav"], # Remove entire tag blocks
remove_forms=True,…
3.3 Link Handling
run_config = CrawlerRunConfig(
exclude_external_links=True, # Remove external links from final content
exclude_social_media_links=True, # Remove links to known social sites…
3.4 Media Filtering
run_config = CrawlerRunConfig(
exclude_external_images=True # Strip images from other domains
)
4.1 Basic Browser Flow
Key Fields:
wait_for:"css:selector"or"js:() => boolean"e.g.js:() => document.querySelectorAll('.item').length > 10.
mean_delay&max_range: define random delays for…
run_config = CrawlerRunConfig(
wait_for="css:.dynamic-content", # Wait for .dynamic-content
delay_before_return_html=2.0, # Wait 2s before capturing final HTML
page_timeout=60000,…
4.2 JavaScript Execution
js_codecan be a single string or a list of strings.js_only=Truemeans “I’m continuing in the same session with new JS steps, no new full navigation.”
run_config = CrawlerRunConfig(
js_code=[
"window.scrollTo(0, document.body.scrollHeight);",
"document.querySelector('.load-more')?.click();"
],
js_only=False
)
4.3 Anti-Bot
magic=Truetries multiple stealth features.simulate_user=Truemimics mouse movements or random delays.override_navigator=Truefakes some navigator properties (like user agent checks).
run_config = CrawlerRunConfig(
magic=True,
simulate_user=True,
override_navigator=True
)
5. Session Management
If re-used in subsequent arun() calls, the same tab/page context is continued (helpful for multi-step tasks or stateful browsing).
run_config = CrawlerRunConfig(
session_id="my_session123"
)
6. Screenshot, PDF & Media Options
Where they appear:
result.screenshot→ Base64 screenshot string.result.pdf→ Byte array with PDF data.
run_config = CrawlerRunConfig(
screenshot=True, # Grab a screenshot as base64
screenshot_wait_for=1.0, # Wait 1s before capturing
pdf=True, # Also…
7. Extraction Strategy
The extracted data will appear in result.extracted_content.
run_config = CrawlerRunConfig(
extraction_strategy=my_css_or_llm_strategy
)
8. Comprehensive Example
Below is a snippet combining many parameters:
What we covered:
- Crawling the main content region, ignoring external links.
- Running JavaScript to click “.show-more”.
- Waiting…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def main():
# Example schema
schema = {
"name":…
9. Best Practices
- Use
BrowserConfigfor global browser settings (headless, user agent). - Use
CrawlerRunConfigto handle the specific crawl needs: content filtering, caching, JS, screenshot,…
10. Conclusion
All parameters that used to be direct arguments to arun() now belong in CrawlerRunConfig . This approach:
- Makes code clearer and more maintainable.
- Minimizes confusion about…
arun_many()
Reference for the arun_many() function in Crawl4AI, which crawls multiple URLs concurrently or in batches, with support for dispatchers, streaming, and per-URL configurations.
Function Signature
async def arun_many(
urls: Union[List[str], List[Any]],
config: Optional[Union[CrawlerRunConfig, List[CrawlerRunConfig]]] = None,
dispatcher: Optional[BaseDispatcher] = None,
...
) ->…
Differences from arun()
- Multiple URLs:
- Instead of crawling a single URL, you pass a list of them (strings or tasks).
- The function returns
RunManyReturnwhich contains either a list ofCrawlResultor an…
Basic Example (Batch Mode)
# Minimal usage: The default dispatcher will be used
results = await crawler.arun_many(
urls=["https://site1.com", "https://site2.com"],
config=CrawlerRunConfig(stream=False) # Default…
Streaming Example
config = CrawlerRunConfig(
stream=True, # Enable streaming mode
cache_mode=CacheMode.BYPASS
)
# Process results as they complete
async for result in await crawler.arun_many(…
With a Custom Dispatcher
dispatcher = MemoryAdaptiveDispatcher(
memory_threshold_percent=70.0,
max_session_permit=10
)
results = await crawler.arun_many(
urls=["https://site1.com", "https://site2.com",…
URL-Specific Configurations
Instead of using one config for all URLs, provide a list of configs with url_matcher patterns:
URL Matching Features:
- String patterns:
"*.pdf","*/blog/*","*python.org*"-…
from crawl4ai import CrawlerRunConfig, MatchMode
from crawl4ai.processors.pdf import PDFContentScrapingStrategy
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
from…
Return Value
Returns a RunManyReturn object which contains either a list of CrawlResult objects, or an async generator if streaming is enabled. You can iterate to check…
Dispatcher Reference
MemoryAdaptiveDispatcher: Dynamically manages concurrency based on system memory usage.SemaphoreDispatcher: Fixed concurrency limit, simpler but less adaptive.
For advanced usage…
Common Pitfalls
- Large Lists : If you pass thousands of URLs, be mindful of memory or rate-limits. A dispatcher can help.
- Session Reuse : If you need specialized logins or persistent contexts, ensure…
Conclusion
Use arun_many() when you want to crawl multiple URLs simultaneously or in controlled parallel tasks. If you need advanced concurrency features (like memory-based adaptive throttling or complex…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| urls | Union[List[str], List[Any]] | A list of URLs (or tasks) to crawl. | Yes | |
| config | Optional[Union[CrawlerRunConfig, List[CrawlerRunConfig]]] | Either a single CrawlerRunConfig applying to all URLs, or a list of CrawlerRunConfig objects with url_matcher patterns. | None | No |
| dispatcher | Optional[BaseDispatcher] | A concurrency controller (e.g. MemoryAdaptiveDispatcher). | None | No |
AsyncWebCrawler - Crawl4AI Documentation (v0.9.x)
Extraction fallback content.
AsyncWebCrawler - Crawl4AI Documentation (v0.9.x)
AsyncWebCrawler
The AsyncWebCrawler is the core class for asynchronous web crawling in Crawl4AI. You typically create it once , optionally customize it with a BrowserConfig (e.g., headless, user agent), then run multiple arun() calls with different CrawlerRunConfig objects.
Recommended usage :
1. Create a BrowserConfig for global browser settings.
2. Instantiate AsyncWebCrawler(config=browser_config).
3. Use the crawler in an async context manager (async with) or manage start/close manually.
4. Call arun(url, config=crawler_run_config) for each page you want.
1. Constructor Overview
Notes :
- Legacy parameters like
always_bypass_cacheremain for backward compatibility, but prefer to set caching inCrawlerRunConfig.
2. Lifecycle: Start/Close or Context Manager
2.1 Context Manager (Recommended)
When the async with block ends, the crawler cleans up (closes the browser, etc.).
2.2 Manual Start & Close
Use this style if you have a long-running application or need full control of the crawler’s lifecycle.
3. Primary Method: arun()
3.1 New Approach
You pass a CrawlerRunConfig object that sets up everything about a crawl—content filtering, caching, session reuse, JS code, screenshots, etc.
3.2 Legacy Parameters Still Accepted
For backward compatibility, arun() can still accept direct arguments like css_selector=..., word_count_threshold=..., etc., but we strongly advise migrating them into a CrawlerRunConfig .
4. Batch Processing: arun_many()
4.1 Resource-Aware Crawling
The arun_many() method now uses an intelligent dispatcher that:
-
Monitors system memory usage
-
Implements adaptive rate limiting
-
Provides detailed progress monitoring
-
Manages concurrent crawls efficiently
4.2 Example Usage
Check page Multi-url Crawling for a detailed example of how to use arun_many().
Explanation :
-
We define a
BrowserConfigwith Firefox, no headless, andverbose=True. -
We define a
CrawlerRunConfigthat bypasses cache , uses a CSS extraction schema, has aword_count_threshold=15, etc. -
We pass them to
AsyncWebCrawler(config=...)andarun(url=..., config=...).
7. Best Practices & Migration Notes
1. Use BrowserConfig for global settings about the browser’s environment.
2. Use CrawlerRunConfig for per-crawl logic (caching, content filtering, extraction strategies, wait conditions).
3. Avoid legacy parameters like css_selector or word_count_threshold directly in arun(). Instead:
4. Context Manager usage is simplest unless you want a persistent crawler across many calls.
8. Summary
AsyncWebCrawler is your entry point to asynchronous crawling:
-
Constructor accepts
BrowserConfig(or defaults). -
arun(url, config=CrawlerRunConfig)is the main method for single-page crawls. -
arun_many(urls, config=CrawlerRunConfig)handles concurrency across multiple URLs. -
For advanced lifecycle control, use
start()andclose()explicitly.
Migration :
- If you used
AsyncWebCrawler(browser_type="chromium", css_selector="..."), move browser settings toBrowserConfig(...)and content/crawl logic toCrawlerRunConfig(...).
This modular approach ensures your code is clean , scalable , and easy to maintain . For any advanced or rarely used parameters, see the BrowserConfig docs.
class AsyncWebCrawler:
def __init__(
self,
crawler_strategy: Optional[AsyncCrawlerStrategy] = None,
config: Optional[BrowserConfig] = None,
always_bypass_cache: bool = False, # deprecated
always_by_pass_cache: Optional[bool] = None, # also deprecated
base_directory: str = ...,
thread_safe: bool = False,
**kwargs,
):
"""
Create an AsyncWebCrawler instance.
Args:
crawler_strategy:
(Advanced) Provide a custom crawler strategy if needed.
config:
A BrowserConfig object specifying how the browser is set up.
always_bypass_cache:
(Deprecated) Use CrawlerRunConfig.cache_mode instead.
base_directory:
Folder for storing caches/logs (if relevant).
thread_safe:
If True, attempts some concurrency safeguards. Usually False.
**kwargs:
Additional legacy or debugging parameters.
"""
)
### Typical Initialization
```python
from crawl4ai import AsyncWebCrawler, BrowserConfig
browser_cfg = BrowserConfig(
browser_type="chromium",
headless=True,
verbose=True
)
crawler = AsyncWebCrawler(config=browser_cfg)
async with AsyncWebCrawler(config=browser_cfg) as crawler:
result = await crawler.arun("https://example.com")
# The crawler automatically starts/closes resources
crawler = AsyncWebCrawler(config=browser_cfg)
await crawler.start()
result1 = await crawler.arun("https://example.com")
result2 = await crawler.arun("https://another.com")
await crawler.close()
async def arun(
self,
url: str,
config: Optional[CrawlerRunConfig] = None,
# Legacy parameters for backward compatibility...
) -> RunManyReturn:
...
import asyncio
from crawl4ai import CrawlerRunConfig, CacheMode
run_cfg = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
css_selector="main.article",
word_count_threshold=10,
screenshot=True
)
async with AsyncWebCrawler(config=browser_cfg) as crawler:
result = await crawler.arun("https://example.com/news", config=run_cfg)
print("Crawled HTML length:", len(result.cleaned_html))
if result.screenshot:
print("Screenshot base64 length:", len(result.screenshot))
async def arun_many(
self,
urls: List[str],
config: Optional[CrawlerRunConfig] = None,
# Legacy parameters maintained for backwards compatibility...
) -> RunManyReturn:
"""
Process multiple URLs with intelligent rate limiting and resource monitoring.
"""
### 4.3 Key Features
1. **Rate Limiting**
- Automatic delay between requests
- Exponential backoff on rate limit detection
- Domain-specific rate limiting
- Configurable retry strategy
2. **Resource Monitoring**
- Memory usage tracking
- Adaptive concurrency based on system load
- Automatic pausing when resources are constrained
3. **Progress Monitoring**
- Detailed or aggregated progress display
- Real-time status updates
- Memory usage statistics
4. **Error Handling**
- Graceful handling of rate limits
- Automatic retries with backoff
- Detailed error reporting
---
## 5. `CrawlResult` Output
Each `arun()` returns a **`CrawlResult`** containing:
- `url`: Final URL (if redirected).
- `html`: Original HTML.
- `cleaned_html`: Sanitized HTML.
- `markdown_v2`: Removed in v0.5. Accessing it raises `AttributeError`; use `markdown`.
- `extracted_content`: If an extraction strategy was used (JSON for CSS/LLM strategies).
- `screenshot`, `pdf`: If screenshots/PDF requested.
- `media`, `links`: Information about discovered images/links.
- `success`, `error_message`: Status info.
For details, see [CrawlResult doc](./crawl-result.md).
---
## 6. Quick Example
Below is an example hooking it all together:
```python
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
import json
async def main():
# 1. Browser config
browser_cfg = BrowserConfig(
browser_type="firefox",
headless=False,
verbose=True
)
# 2. Run config
schema = {
"name": "Articles",
"baseSelector": "article.post",
"fields": [
{
"name": "title",
"selector": "h2",
"type": "text"
},
{
"name": "url",
"selector": "a",
"type": "attribute",
"attribute": "href"
}
]
}
run_cfg = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
extraction_strategy=JsonCssExtractionStrategy(schema),
word_count_threshold=15,
remove_overlay_elements=True,
wait_for="css:.post" # Wait for posts to appear
)
async with AsyncWebCrawler(config=browser_cfg) as crawler:
result = await crawler.arun(
url="https://example.com/blog",
config=run_cfg
)
if result.success:
print("Cleaned HTML length:", len(result.cleaned_html))
if result.extracted_content:
articles = json.loads(result.extracted_content)
print("Extracted articles:", articles[:2])
else:
print("Error:", result.error_message)
asyncio.run(main())
run_cfg = CrawlerRunConfig(css_selector=".main-content", word_count_threshold=20)
result = await crawler.arun(url="...", config=run_cfg)
C4A-Script API Reference
Complete reference for all C4A-Script commands, syntax, and advanced features, including navigation, waiting, mouse, keyboard, control flow, variables, procedures, and integration with Crawl4AI.
Command Categories
Complete reference for all C4A-Script commands, syntax, and advanced features.
🧭 Navigation Commands
Navigate between pages and manage browser history.
`GO <url>`
Navigate to a specific URL.
Syntax:
GO <url>
Parameters:
url- Target URL (string)
Examples:
GO https://example.com
GO https://api.example.com/login
GO…
GO <url>
GO https://example.com
GO https://api.example.com/login
GO /relative/path
`RELOAD`
Refresh the current page.
Syntax:
RELOAD
Examples:
RELOAD
Notes:
- Equivalent to pressing F5 or clicking browser refresh
- Waits for page reload to complete -…
RELOAD
RELOAD
`BACK`
Navigate back in browser history.
Syntax:
BACK
Examples:
BACK
Notes:
- Equivalent to clicking browser back button
- Does nothing if no previous page exists
- Waits…
BACK
BACK
`FORWARD`
Navigate forward in browser history.
Syntax:
FORWARD
Examples:
FORWARD
Notes:
- Equivalent to clicking browser forward button
- Does nothing if no next page exists -…
FORWARD
FORWARD
⏱️ Wait Commands
Control timing and synchronization with page elements.
`WAIT <time>`
Wait for a specified number of seconds.
Syntax:
WAIT <seconds>
Parameters:
seconds- Number of seconds to wait (number)
Examples:
WAIT 3
WAIT 1.5
WAIT…
WAIT <seconds>
WAIT 3
WAIT 1.5
WAIT 10
`WAIT <selector> <timeout>`
Wait for an element to appear on the page.
Syntax:
WAIT `<selector>` <timeout>
Parameters:
selector- CSS selector for the element (string in backticks)timeout- Maximum…
WAIT `<selector>` <timeout>
WAIT `#content` 10
WAIT `.loading-spinner` 5
WAIT `button[type="submit"]` 15
WAIT `.results .item:first-child` 8
`WAIT "<text>" <timeout>`
Wait for specific text to appear anywhere on the page.
Syntax:
WAIT "<text>" <timeout>
Parameters:
text- Text content to wait for (string in quotes)timeout- Maximum…
WAIT "<text>" <timeout>
WAIT "Loading complete" 10
WAIT "Welcome back" 5
WAIT "Search results" 15
🖱️ Mouse Commands
Simulate mouse interactions and movements.
`CLICK <selector>`
Click on an element specified by CSS selector.
Syntax:
CLICK `<selector>`
Parameters:
selector- CSS selector for the element (string in backticks)
Examples:
CLICK…
CLICK `<selector>`
CLICK `#submit-button`
CLICK `.menu-item:first-child`
CLICK `button[data-action="save"]`
CLICK `a[href="/dashboard"]`
`CLICK <x> <y>`
Click at specific coordinates on the page.
Syntax:
CLICK <x> <y>
Parameters:
x- X coordinate in pixels (number)y- Y coordinate in pixels…
CLICK <x> <y>
CLICK 100 200
CLICK 500 300
CLICK 0 0
`DOUBLE_CLICK <selector>`
Double-click on an element.
Syntax:
DOUBLE_CLICK `<selector>`
Parameters:
selector- CSS selector for the element (string in backticks)
Examples:
DOUBLE_CLICK…
DOUBLE_CLICK `<selector>`
DOUBLE_CLICK `.file-icon`
DOUBLE_CLICK `#editable-cell`
DOUBLE_CLICK `.expandable-item`
`RIGHT_CLICK <selector>`
Right-click on an element to open context menu.
Syntax:
RIGHT_CLICK `<selector>`
Parameters:
selector- CSS selector for the element (string in…
RIGHT_CLICK `<selector>`
RIGHT_CLICK `#context-target`
RIGHT_CLICK `.menu-trigger`
RIGHT_CLICK `img.thumbnail`
`SCROLL <direction> <amount>`
Scroll the page in a specified direction.
Syntax:
SCROLL <direction> <amount>
Parameters:
direction- Direction to scroll:UP,DOWN,LEFT,RIGHTamount- Number of…
SCROLL <direction> <amount>
SCROLL DOWN 500
SCROLL UP 200
SCROLL LEFT 100
SCROLL RIGHT 300
`MOVE <x> <y>`
Move mouse cursor to specific coordinates.
Syntax:
MOVE <x> <y>
Parameters:
x- X coordinate in pixels (number)y- Y coordinate in pixels (number)
Examples:
MOVE…
MOVE <x> <y>
MOVE 200 100
MOVE 500 400
`DRAG <x1> <y1> <x2> <y2>`
Drag from one point to another.
Syntax:
DRAG <x1> <y1> <x2> <y2>
Parameters:
x1,y1- Starting coordinates (numbers)x2,y2- Ending coordinates…
DRAG <x1> <y1> <x2> <y2>
DRAG 100 100 500 300
DRAG 0 200 400 200
⌨️ Keyboard Commands
Simulate keyboard input and key presses.
`TYPE "<text>"`
Type text into the currently focused element.
Syntax:
TYPE "<text>"
Parameters:
text- Text to type (string in quotes)
Examples:
TYPE "Hello, World!"
TYPE…
TYPE "<text>"
TYPE "Hello, World!"
TYPE "user@example.com"
TYPE "Password123!"
`TYPE $<variable>`
Type the value of a variable.
Syntax:
TYPE $<variable>
Parameters:
variable- Variable name (without quotes)
Examples:
SETVAR email = "user@example.com"
TYPE…
TYPE $<variable>
SETVAR email = "user@example.com"
TYPE $email
`PRESS <key>`
Press and release a special key.
Syntax:
PRESS <key>
Parameters:
key- Key name (see supported keys below)
Supported Keys:
Tab,Enter,Escape,SpaceArrowUp,…
PRESS <key>
PRESS Tab
PRESS Enter
PRESS Escape
PRESS ArrowDown
`KEY_DOWN <key>`
Hold down a modifier key.
Syntax:
KEY_DOWN <key>
Parameters:
key- Modifier key:Shift,Control,Alt,Meta
Examples:
KEY_DOWN Shift
KEY_DOWN…
KEY_DOWN <key>
KEY_DOWN Shift
KEY_DOWN Control
`KEY_UP <key>`
Release a modifier key.
Syntax:
KEY_UP <key>
Parameters:
key- Modifier key:Shift,Control,Alt,Meta
Examples:
KEY_UP Shift
KEY_UP Control
Notes: -…
KEY_UP <key>
KEY_UP Shift
KEY_UP Control
`CLEAR <selector>`
Clear the content of an input field.
Syntax:
CLEAR `<selector>`
Parameters:
selector- CSS selector for input element (string in backticks)
Examples:
CLEAR…
CLEAR `<selector>`
CLEAR `#search-box`
CLEAR `input[name="email"]`
CLEAR `.form-input:first-child`
`SET <selector> "<value>"`
Set the value of an input field directly.
Syntax:
SET `<selector>` "<value>"
Parameters:
selector- CSS selector for input element (string in backticks)value- Value to…
SET `<selector>` "<value>"
SET `#email` "user@example.com"
SET `#age` "25"
SET `textarea#message` "Hello, this is a test message."
🔀 Control Flow Commands
Add conditional logic and loops to your scripts.
`IF (EXISTS <selector>) THEN <command>`
Execute command if element exists.
Syntax:
IF (EXISTS `<selector>`) THEN <command>
Parameters:
selector- CSS selector to check (string in backticks)command- Command to…
IF (EXISTS `<selector>`) THEN <command>
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept-cookies`
IF (EXISTS `#popup-modal`) THEN CLICK `.close-button`
IF (EXISTS `.error-message`) THEN RELOAD
`IF (EXISTS <selector>) THEN <command> ELSE <command>`
Execute command based on element existence.
Syntax:
IF (EXISTS `<selector>`) THEN <command> ELSE <command>
Parameters:
selector- CSS selector to check (string in backticks) -…
IF (EXISTS `<selector>`) THEN <command> ELSE <command>
IF (EXISTS `.user-menu`) THEN CLICK `.logout` ELSE CLICK `.login`
IF (EXISTS `.loading`) THEN WAIT 5 ELSE CLICK `#continue`
`IF (NOT EXISTS <selector>) THEN <command>`
Execute command if element does not exist.
Syntax:
IF (NOT EXISTS `<selector>`) THEN <command>
Parameters:
selector- CSS selector to check (string in backticks)command-…
IF (NOT EXISTS `<selector>`) THEN <command>
IF (NOT EXISTS `.logged-in`) THEN GO /login
IF (NOT EXISTS `.results`) THEN CLICK `#search-button`
`IF (<javascript>) THEN <command>`
Execute command based on JavaScript condition.
Syntax:
IF (`<javascript>`) THEN <command>
Parameters:
javascript- JavaScript expression that returns boolean (string in…
IF (`<javascript>`) THEN <command>
IF (`window.innerWidth < 768`) THEN CLICK `.mobile-menu`
IF (`document.readyState === "complete"`) THEN CLICK `#start`
IF (`localStorage.getItem("user")`) THEN GO /dashboard
`REPEAT (<command>, <count>)`
Repeat a command a specific number of times.
Syntax:
REPEAT (<command>, <count>)
Parameters:
command- Command to repeatcount- Number of times to repeat…
REPEAT (<command>, <count>)
REPEAT (SCROLL DOWN 300, 5)
REPEAT (PRESS Tab, 3)
REPEAT (CLICK `.load-more`, 10)
`REPEAT (<command>, <condition>)`
Repeat a command while condition is true.
Syntax:
REPEAT (<command>, `<condition>`)
Parameters:
command- Command to repeatcondition- JavaScript condition to check…
REPEAT (<command>, `<condition>`)
REPEAT (SCROLL DOWN 500, `document.querySelector(".load-more")`)
REPEAT (PRESS ArrowDown, `window.scrollY < document.body.scrollHeight`)
💾 Variables and Data
Store and manipulate data within scripts.
`SETVAR <name> = "<value>"`
Create or update a variable.
Syntax:
SETVAR <name> = "<value>"
Parameters:
name- Variable name (alphanumeric, underscore)value- Variable value (string in…
SETVAR <name> = "<value>"
SETVAR username = "john@example.com"
SETVAR password = "secret123"
SETVAR base_url = "https://api.example.com"
SETVAR counter = "0"
`EVAL <javascript>`
Execute arbitrary JavaScript code.
Syntax:
EVAL `<javascript>`
Parameters:
javascript- JavaScript code to execute (string in backticks)
Examples:
EVAL…
EVAL `<javascript>`
EVAL `console.log("Script started")`
EVAL `window.scrollTo(0, 0)`
EVAL `localStorage.setItem("test", "value")`
EVAL `document.title = "Automated Test"`
📝 Comments and Documentation
`# <comment>`
Add comments to scripts for documentation.
Syntax:
# <comment text>
Examples:
# This script logs into the application
# Step 1: Navigate to login page
GO /login
# Step 2:…
# <comment text>
# This script logs into the application
# Step 1: Navigate to login page
GO /login
# Step 2: Fill credentials
TYPE "user@example.com"
🔧 Procedures (Advanced)
Define reusable command sequences.
`PROC <name> ... ENDPROC`
Define a reusable procedure.
Syntax:
PROC <name>
<commands>
ENDPROC
Parameters:
name- Procedure name (alphanumeric, underscore)commands- Commands to include in…
PROC <name>
<commands>
ENDPROC
PROC login
CLICK `#email`
TYPE $email
CLICK `#password`
TYPE $password
CLICK `#submit`
ENDPROC
PROC handle_popups
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept`
IF (EXISTS…
`<procedure_name>`
Call a defined procedure.
Syntax:
<procedure_name>
Examples:
# Define procedure first
PROC setup
GO /login
WAIT `#form` 5
ENDPROC
# Call…
<procedure_name>
# Define procedure first
PROC setup
GO /login
WAIT `#form` 5
ENDPROC
# Call procedure
setup
login
Error Handling Best Practices
1. Always Use Waits
# Bad - element might not be ready
CLICK `#button`
# Good - wait for element first
WAIT `#button` 5
CLICK `#button`
2. Handle Optional Elements
# Check before interacting
IF (EXISTS `.popup`) THEN CLICK `.close`
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept`
# Then proceed with main flow
CLICK `#main-action`
3. Use Descriptive Variables
# Set up reusable data
SETVAR admin_email = "admin@company.com"
SETVAR test_password = "TestPass123!"
SETVAR staging_url = "https://staging.example.com"
# Use throughout script
GO $staging_url
TYPE…
4. Add Debugging Information
# Log progress
EVAL `console.log("Starting login process")`
GO /login
# Verify page state
IF (`document.title.includes("Login")`) THEN EVAL `console.log("On login page")`
# Continue with login
TYPE…
Common Patterns
Login Flow
# Complete login automation
SETVAR email = "user@example.com"
SETVAR password = "mypassword"
GO /login
WAIT `#login-form` 5
# Handle optional cookie banner
IF (EXISTS `.cookie-banner`) THEN CLICK…
Infinite Scroll
# Load all content with infinite scroll
GO /products
# Scroll and load more content
REPEAT (SCROLL DOWN 500, `document.querySelector(".load-more")`)
# Alternative: Fixed number of scrolls
REPEAT…
Form Validation
# Handle form with validation
SET `#email` "invalid-email"
CLICK `#submit`
# Check for validation error
IF (EXISTS `.error-email`) THEN SET `#email` "valid@example.com"
# Retry submission
CLICK…
Multi-step Process
# Complex multi-step workflow
PROC navigate_to_step
CLICK `.next-button`
WAIT `.step-content` 5
ENDPROC
# Step 1
WAIT `.step-1` 5
SET `#name` "John Doe"
navigate_to_step
# Step 2
SET `#email`…
Integration with Crawl4AI
Use C4A-Script with Crawl4AI for dynamic content interaction:
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
# Define interaction script
script = """
# Handle dynamic content loading
WAIT `.content` 5
IF (EXISTS `.load-more-button`) THEN CLICK…
Conclusion
This reference covers all available C4A-Script commands and patterns. For interactive learning, try the tutorial or [live…
CrawlResult Reference
Reference for the CrawlResult class in Crawl4AI, detailing all fields returned after a crawl operation, including content, metadata, links, media, and optional captures.
Overview
The CrawlResult class encapsulates everything returned after a single crawl operation. It provides the raw or processed content, details on links and media, plus optional metadata (like…
class CrawlResult(BaseModel):
url: str
html: str
success: bool
cleaned_html: Optional[str] = None
fit_html: Optional[str] = None # Preprocessed HTML optimized for extraction…
1. Basic Crawl Info
1.1 url (str)
What: The final crawled URL (after any redirects).
1.2 success (bool)
What: True if the crawl pipeline ended without major errors; False otherwise.
1.3…
print(result.url) # e.g., "https://example.com/"
if not result.success:
print(f"Crawl failed: {result.error_message}")
if result.status_code == 404:
print("Page not found!")
if result.status_code in (301, 302) and result.redirected_status_code == 200:
print(f"Redirected to {result.redirected_url} (OK)")
if not result.success:
print("Error:", result.error_message)
# If you used session_id="login_session" in CrawlerRunConfig, see it here:
print("Session:", result.session_id)
if result.response_headers:
print("Server:", result.response_headers.get("Server", "Unknown"))
if result.ssl_certificate:
print("Issuer:", result.ssl_certificate.issuer)
2. Raw / Cleaned Content
2.1 html (str)
What: The original unmodified HTML from the final page load.
2.2 cleaned_html (Optional[str])
What: A sanitized HTML version—scripts, styles, or excluded…
# Possibly large
print(len(result.html))
print(result.cleaned_html[:500]) # Show a snippet
3. Markdown Fields
3.1 The Markdown Generation Approach
Crawl4AI can convert HTML→Markdown, optionally including:
- Raw markdown
- Links as citations (with a references section)
- Fit markdown if a…
if result.markdown:
md_res = result.markdown
print("Raw MD:", md_res.raw_markdown[:300])
print("Citations MD:", md_res.markdown_with_citations[:300])
print("References:",…
print(result.markdown.raw_markdown[:200])
print(result.markdown.fit_markdown)
print(result.markdown.fit_html)
4. Media & Links
4.1 media (Dict[str, List[Dict]])
What: Contains info about discovered images, videos, or audio. Typically keys: "images", "videos", "audios".
Common Fields in each item: -…
images = result.media.get("images", [])
for img in images:
if img.get("score", 0) > 5:
print("High-value image:", img["src"])
for link in result.links["internal"]:
print(f"Internal link to {link['href']} with text {link['text']}")
5. Additional Fields
5.1 extracted_content (Optional[str])
What: If you used extraction_strategy (CSS, LLM, etc.), the structured output (JSON).
5.2 downloaded_files (Optional[List[str]])
What:…
if result.extracted_content:
data = json.loads(result.extracted_content)
print(data)
if result.downloaded_files:
for file_path in result.downloaded_files:
print("Downloaded:", file_path)
import base64
if result.screenshot:
with open("page.png", "wb") as f:
f.write(base64.b64decode(result.screenshot))
if result.pdf:
with open("page.pdf", "wb") as f:
f.write(result.pdf)
if result.mhtml:
with open("page.mhtml", "w", encoding="utf-8") as f:
f.write(result.mhtml)
if result.metadata:
print("Title:", result.metadata.get("title"))
print("Author:", result.metadata.get("author"))
6. `dispatch_result` (optional)
A DispatchResult object providing additional concurrency and resource usage information when crawling URLs in parallel (e.g., via arun_many() with custom dispatchers). It contains:
task_id:…
# Example usage:
for result in results:
if result.success and result.dispatch_result:
dr = result.dispatch_result
print(f"URL: {result.url}, Task ID: {dr.task_id}")…
7. Network Requests & Console Messages
When you enable network and console message capturing in CrawlerRunConfig using capture_network_requests=True and capture_console_messages=True, the CrawlResult will include these…
if result.network_requests:
# Count different types of events
requests = [r for r in result.network_requests if r.get("event_type") == "request"]
responses = [r for r in…
if result.console_messages:
# Count messages by type
message_types = {}
for msg in result.console_messages:
msg_type = msg.get("type", "unknown")
message_types[msg_type] =…
8. Example: Accessing Everything
async def handle_result(result: CrawlResult):
if not result.success:
print("Crawl error:", result.error_message)
return
# Basic info
print("Crawled URL:", result.url)…
9. Key Points & Future
- Deprecated legacy properties of CrawlResult
markdown_v2- Removed in v0.5 and now raisesAttributeError. Useresult.markdowninstead.fit_markdownandfit_html- No longer…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| url | str | The final crawled URL (after any redirects). | Yes | |
| html | str | The original unmodified HTML from the final page load. | Yes | |
| success | bool | True if the crawl pipeline ended without major errors; False otherwise. | Yes | |
| cleaned_html | Optional[str] | A sanitized HTML version—scripts, styles, or excluded tags are removed based on your CrawlerRunConfig. | None | No |
| fit_html | Optional[str] | Preprocessed HTML optimized for extraction | None | No |
| media | Dict[str, List[Dict]] | Contains info about discovered images, videos, or audio. Typically keys: 'images', 'videos', 'audios'. | {} | No |
| links | Dict[str, List[Dict]] | Holds internal and external link data. Usually two keys: 'internal' and 'external'. | {} | No |
| downloaded_files | Optional[List[str]] | If accept_downloads=True in your BrowserConfig + downloads_path, lists local file paths for downloaded items. | None | No |
| screenshot | Optional[str] | Base64-encoded screenshot if screenshot=True in CrawlerRunConfig. | None | No |
| Optional[bytes] | Raw PDF bytes if pdf=True in CrawlerRunConfig. | None | No | |
| mhtml | Optional[str] | MHTML snapshot of the page if capture_mhtml=True in CrawlerRunConfig. | None | No |
| markdown | Optional[Union[str, MarkdownGenerationResult]] | Holds the MarkdownGenerationResult. | None | No |
| extracted_content | Optional[str] | If you used extraction_strategy (CSS, LLM, etc.), the structured output (JSON). | None | No |
| metadata | Optional[dict] | Page-level metadata if discovered (title, description, OG data, etc.). | None | No |
| error_message | Optional[str] | If success=False, a textual description of the failure. | None | No |
| session_id | Optional[str] | The ID used for reusing a browser context across multiple calls. | None | No |
| response_headers | Optional[dict] | Final HTTP response headers. | None | No |
| status_code | Optional[int] | The page's HTTP status code (e.g., 200, 404). When the page was reached via redirect, this is the status code of the first response in the redirect chain. | None | No |
| redirected_status_code | Optional[int] | The HTTP status code of the final redirect destination. | None | No |
| ssl_certificate | Optional[SSLCertificate] | If fetch_ssl_certificate=True in your CrawlerRunConfig, contains a SSLCertificate object. | None | No |
| dispatch_result | Optional[DispatchResult] | A DispatchResult object providing additional concurrency and resource usage information when crawling URLs in parallel. | None | No |
| network_requests | Optional[List[Dict[str, Any]]] | A list of dictionaries containing information about all network requests, responses, and failures captured during the crawl. | None | No |
| console_messages | Optional[List[Dict[str, Any]]] | A list of dictionaries containing all browser console messages captured during the crawl. | None | No |
digest()
The digest() method is the primary interface for adaptive web crawling. It intelligently crawls websites starting from a given URL, guided by a query, and automatically determines when sufficient…
Method Signature
async def digest(
start_url: str,
query: str,
resume_from: Optional[Union[str, Path]] = None
) -> CrawlState
Parameters
start_url
- Type :
str - Required : Yes
- Description : The starting URL for the crawl. This should be a valid HTTP/HTTPS URL that serves as the entry point for information…
Return Value
Returns a CrawlState object containing:
- crawled_urls (
Set[str]): All URLs that have been crawled - knowledge_base (
List[CrawlResult]): Collection of crawled pages with content -…
How It Works
The digest() method implements an intelligent crawling algorithm:
- Initial Crawl : Starts from the provided URL
- Link Analysis : Evaluates all discovered links for relevance -…
Examples
async with AsyncWebCrawler() as crawler:
adaptive = AdaptiveCrawler(crawler)
state = await adaptive.digest(
start_url="https://docs.python.org/3/",
query="async await context…
config = AdaptiveConfig(
confidence_threshold=0.9, # Require high confidence
max_pages=30, # Allow more pages
top_k_links=3 # Follow top 3 links per…
# First crawl - may be interrupted
state1 = await adaptive.digest(
start_url="https://example.com",
query="machine learning algorithms"
)
# Save state (if not…
state = await adaptive.digest(
start_url="https://docs.example.com",
query="api reference"
)
# Monitor progress
print(f"Pages crawled: {len(state.crawled_urls)}")
print(f"New terms…
Query Best Practices
- Be Specific : Use descriptive terms that appear in target content
- Include Key Terms : Add technical terms you expect to find
- Multiple Concepts : Combine related concepts for…
# Good
query = "python async context managers implementation"
# Too broad
query = "python programming"
query = "oauth2 jwt refresh tokens authorization"
query = "rest api pagination sorting filtering"
Performance Considerations
- Initial URL : Choose a page with good navigation (e.g., documentation index)
- Query Length : 3-8 terms typically work best
- Link Density : Sites with clear navigation crawl more…
Error Handling
try:
state = await adaptive.digest(
start_url="https://example.com",
query="search terms"
)
except Exception as e:
print(f"Crawl failed: {e}")
# State is auto-saved if…
Stopping Conditions
The crawl stops when any of these conditions are met:
- Confidence Threshold : Reached the configured confidence level
- Page Limit : Crawled the maximum number of pages
- **Diminishing…
See Also
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| start_url | str | The starting URL for the crawl. This should be a valid HTTP/HTTPS URL that serves as the entry point for information gathering. | Yes | |
| query | str | The search query that guides the crawling process. This should contain key terms related to the information you're seeking. The crawler uses this to evaluate relevance and determine which links to… | Yes | |
| resume_from | Optional[Union[str, Path]] | Path to a previously saved crawl state file. When provided, the crawler resumes from the saved state instead of starting fresh. | None | No |
Browser, Crawler & LLM Config - Crawl4AI Documentation (v0.9.x)
This page documents the configuration classes for Crawl4AI: BrowserConfig, CrawlerRunConfig, and LLMConfig, detailing their parameters, usage, and helper methods.
1. BrowserConfig – Controlling the Browser
BrowserConfig focuses on how the browser is launched and behaves. This includes headless mode, proxies, user agents, and other environment tweaks.
from crawl4ai import AsyncWebCrawler, BrowserConfig
browser_cfg = BrowserConfig(
browser_type="chromium",
headless=True,
viewport_width=1280,
viewport_height=720,…
1.1 Parameter Highlights
| Parameter | Type / Default | What It Does |
|---|---|---|
browser_type |
"chromium", "firefox", "webkit" (default: "chromium") |
Which browser engine to use.… |
2. CrawlerRunConfig – Controlling Each Crawl
While BrowserConfig sets up the environment , CrawlerRunConfig details how each crawl operation should behave: caching, content filtering, link or domain blocking, timeouts,…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
run_cfg = CrawlerRunConfig(
wait_for="css:.main-content",
word_count_threshold=15,
excluded_tags=["nav", "footer"],…
2.1 Parameter Highlights
We group them by category.
A) Content Processing
| Parameter | Type / Default | What It Does |
|---|---|---|
word_count_threshold |
int (default: ~200) |
Skips text blocks below X words. Helps ignore trivial sections.… |
B) Browser Location and Identity
| Parameter | Type / Default | What It Does |
|---|---|---|
locale |
str or None (None) |
Browser's locale (e.g., "en-US", "fr-FR") for language preferences. |
| … |
C) Caching & Session
| Parameter | Type / Default | What It Does |
|---|---|---|
cache_mode |
CacheMode or None |
Controls how caching is handled (ENABLED, BYPASS, DISABLED, etc.). If… |
D) Page Navigation & Timing
| Parameter | Type / Default | What It Does |
|---|---|---|
wait_until |
str (domcontentloaded) |
Condition for navigation to "complete". Often "networkidle" or… |
E) Page Interaction
| Parameter | Type / Default | What It Does |
|---|---|---|
js_code |
str or list[str] (None) |
JavaScript to run after wait_for and delay_before_return_html, on… |
F) Media Handling
| Parameter | Type / Default | What It Does |
|---|---|---|
screenshot |
bool (False) |
Capture a screenshot (base64) in result.screenshot. |
screenshot_wait_for… |
G) Link/Domain Handling
| Parameter | Type / Default | What It Does |
|---|---|---|
exclude_social_media_domains |
list (e.g. Facebook/Twitter) |
A default list can be extended. Any link to these… |
H) Debug, Logging & Network Monitoring
| Parameter | Type / Default | What It Does |
|---|---|---|
verbose |
bool (True) |
Prints logs detailing each step of crawling, interactions, or errors. |
| … |
I) Connection & HTTP Parameters
| Parameter | Type / Default | What It Does |
|---|---|---|
method |
str ("GET") |
HTTP method to use when using AsyncHTTPCrawlerStrategy (e.g., "GET", "POST"). |
| … |
J) Virtual Scroll Configuration
| Parameter | Type / Default | What It Does |
|---|---|---|
virtual_scroll_config |
VirtualScrollConfig or dict (None) |
Configuration for handling virtualized scrolling… |
from crawl4ai import VirtualScrollConfig
virtual_config = VirtualScrollConfig(
container_selector="#timeline", # CSS selector for scrollable container
scroll_count=30, #…
K) URL Matching Configuration
| Parameter | Type / Default | What It Does |
|---|---|---|
url_matcher |
UrlMatcher (None) |
Pattern(s) to match URLs against. Can be: string (glob), function, or list of… |
from crawl4ai import CrawlerRunConfig, MatchMode
from crawl4ai.processors.pdf import PDFContentScrapingStrategy
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
# Simple string…
L) Advanced Crawling Features
| Parameter | Type / Default | What It Does |
|---|---|---|
deep_crawl_strategy |
DeepCrawlStrategy or None (None) |
Strategy for deep/recursive crawling. Enables… |
2.2 Helper Methods
Both BrowserConfig and CrawlerRunConfig provide a clone() method to create modified copies:
# Create a base configuration
base_config = CrawlerRunConfig(
cache_mode=CacheMode.ENABLED,
word_count_threshold=200
)
# Create variations using clone()
stream_config =…
Class-Level Defaults (`set_defaults` / `get_defaults` / `reset_defaults`)
Both config classes support class-level default overrides. When deploying in a server or cloud context, this eliminates the need to pass the same parameters at every call site.
**Resolution…
from crawl4ai import BrowserConfig, CrawlerRunConfig
# Set once at application startup
BrowserConfig.set_defaults(
cache_cdp_connection=True,
cdp_close_delay=0,…
2.3 Example Usage
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
async def main():
# Configure the browser
browser_cfg = BrowserConfig(…
2.4 Compliance & Ethics
| Parameter | Type / Default | What It Does |
|---|---|---|
check_robots_txt |
bool (False) |
When True, checks and respects robots.txt rules before crawling. Uses… |
run_config = CrawlerRunConfig(
check_robots_txt=True, # Enable robots.txt compliance
user_agent="MyBot/1.0" # Identify your crawler
)
3. LLMConfig - Setting up LLM providers
LLMConfig is useful to pass LLM provider config to strategies and functions that rely on LLMs to do extraction, filtering, schema generation etc. Currently it can be used in the following -
-…
3.1 Parameters
| Parameter | Type / Default | What It Does |
|---|---|---|
provider |
`"ollama/llama3","groq/llama3-70b-8192","groq/llama3-8b-8192", "openai/gpt-4o-mini"… |
3.2 Example Usage
llm_config = LLMConfig(
provider="openai/gpt-4o-mini",
api_token=os.getenv("OPENAI_API_KEY"),
backoff_base_delay=1, # optional
backoff_max_attempts=5, # optional…
4. Putting It All Together
- Use
BrowserConfigfor global browser settings: engine, headless, proxy, user agent. - Use
CrawlerRunConfigfor each crawl’s context : how to filter content, handle caching,…
# Create a modified copy with the clone() method
stream_cfg = run_cfg.clone(
stream=True,
cache_mode=CacheMode.BYPASS
)
# Or set project-wide defaults once at…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| browser_type | chromium", "firefox", "webkit" | Which browser engine to use. "chromium" is typical for many sites, "firefox" or "webkit" for specialized tests. | chromium | No |
| headless | bool | Headless means no visible UI. False is handy for debugging. | True | No |
| browser_mode | str | How browser is initialized: "dedicated" (new instance), "builtin" (CDP background), "custom" (explicit CDP), "docker" (container). | dedicated | No |
| use_managed_browser | bool | Launch browser via CDP for advanced control. Set automatically based on browser_mode. | False | No |
| cdp_url | str | Chrome DevTools Protocol endpoint URL (e.g., "ws://localhost:9222/devtools/browser/"). Set automatically based on browser_mode. | No | |
| debugging_port | int | Port for browser debugging protocol. | 9222 | No |
| host | str | Host for browser connection. | localhost | No |
| viewport_width | int | Initial page width (in px). Useful for testing responsive layouts. | 1080 | No |
| viewport_height | int | Initial page height (in px). | 600 | No |
| viewport | dict | Viewport dimensions dict. If set, overrides viewport_width and viewport_height. | No | |
| device_scale_factor | float | Device pixel ratio for rendering. Use 2.0 for Retina-quality screenshots. Higher values produce larger images and use more memory. | 1.0 | No |
| proxy | str | Deprecated. Use proxy_config instead. If set, it will be auto-converted internally. | No | |
| proxy_config | ProxyConfig or dict | For advanced or multi-proxy needs, specify ProxyConfig object or dict like {"server": "...", "username": "...", "password": "..."}. | No | |
| use_persistent_context | bool | If True, uses a persistent browser context (keep cookies, sessions across runs). Also sets use_managed_browser=True. | False | No |
| user_data_dir | str or None | Directory to store user data (profiles, cookies). Must be set if you want permanent sessions. | No | |
| chrome_channel | str | Chrome channel to launch (e.g., "chrome", "msedge"). Only for browser_type="chromium". Auto-set to empty for Firefox/WebKit. | chromium | No |
| channel | str | Alias for chrome_channel. | chromium | No |
| accept_downloads | bool | Whether to allow file downloads. Requires downloads_path if True. | False | No |
| downloads_path | str or None | Directory to store downloaded files. | No | |
| storage_state | str or dict or None | In-memory storage state (cookies, localStorage) to restore browser state. | No | |
| ignore_https_errors | bool | If True, continues despite invalid certificates (common in dev/staging). | True | No |
| java_script_enabled | bool | Disable if you want no JS overhead, or if only static content is needed. | True | No |
| sleep_on_close | bool | Add a small delay when closing browser (can help with cleanup issues). | False | No |
| cookies | list | Pre-set cookies, each a dict like {"name": "session", "value": "...", "url": "..."}. | [] | No |
| headers | dict | Extra HTTP headers for every request, e.g. {"Accept-Language": "en-US"}. | {} | No |
| user_agent | str | Your custom user agent string. | Chrome-based UA | No |
| user_agent_mode | str | Set to "random" to randomize user agent from a pool (helps with bot detection). | No | |
| user_agent_generator_config | dict | Configuration dict for user agent generation when user_agent_mode="random". | {} | No |
| text_mode | bool | If True, tries to disable images/other heavy content for speed. | False | No |
| light_mode | bool | Disables some background features for performance gains. | False | No |
| avoid_ads | bool | If True, blocks requests to common ad/tracker domains (Google Analytics, DoubleClick, Facebook, Hotjar, etc.) at the browser context level. | False | No |
| avoid_css | bool | If True, blocks loading of CSS files (.css, .less, .scss, .sass) for faster, leaner crawls when only text content is needed. | False | No |
| extra_args | list | Additional flags for the underlying browser process, e.g. ["--disable-extensions"]. | [] | No |
| enable_stealth | bool | Enable playwright-stealth mode to bypass bot detection. Cannot be used with browser_mode="builtin". | False | No |
| word_count_threshold | int | Skips text blocks below X words. Helps ignore trivial sections. | 200 | No |
| extraction_strategy | ExtractionStrategy | If set, extracts structured data (CSS-based, LLM-based, etc.). | No | |
| chunking_strategy | ChunkingStrategy | Strategy to chunk content before extraction. Can be customized for different chunking approaches. | RegexChunking() | No |
| markdown_generator | MarkdownGenerationStrategy | If you want specialized markdown output (citations, filtering, chunking, etc.). Can be customized with options such as content_source parameter to select the HTML input source ('cleaned_html',… | No | |
| css_selector | str | Retains only the part of the page matching this selector. Affects the entire extraction process. | No | |
| target_elements | List[str] | List of CSS selectors for elements to focus on for markdown generation and data extraction, while still processing the entire page for links, media, etc. Provides more flexibility than css_selector. | No | |
| excluded_tags | list | Removes entire tags (e.g. ["script", "style"]). | No | |
| excluded_selector | str | Like css_selector but to exclude. E.g. "#ads, .tracker". | No | |
| only_text | bool | If True, tries to extract text-only content. | False | No |
| prettiify | bool | If True, beautifies final HTML (slower, purely cosmetic). | False | No |
| keep_data_attributes | bool | If True, preserve data-* attributes in cleaned HTML. | False | No |
| keep_attrs | list | List of HTML attributes to keep during processing (e.g., ["id", "class", "data-value"]). | [] | No |
| remove_forms | bool | If True, remove all <form> elements. | False | No |
| parser_type | str | HTML parser to use (e.g., "lxml", "html.parser"). | lxml | No |
| scraping_strategy | ContentScrapingStrategy | Strategy to use for content scraping. Can be customized for different scraping needs (e.g., PDF extraction). | LXMLWebScrapingStrategy() | No |
| locale | str or None | Browser's locale (e.g., "en-US", "fr-FR") for language preferences. | No | |
| timezone_id | str or None | Browser's timezone (e.g., "America/New_York", "Europe/Paris"). | No | |
| geolocation | GeolocationConfig or None | GPS coordinates configuration. Use GeolocationConfig(latitude=..., longitude=..., accuracy=...). | No | |
| fetch_ssl_certificate | bool | If True, fetches and includes SSL certificate information in the result. | False | No |
| proxy_config | ProxyConfig, list[ProxyConfig], or None | Proxy configuration for this specific crawl. Pass a single proxy or an ordered list of proxies to try. See Anti-Bot & Fallback. | No | |
| proxy_rotation_strategy | ProxyRotationStrategy | Strategy for rotating proxies during crawl operations. | No | |
| max_retries | int | Number of retry rounds when anti-bot blocking is detected. Each round tries all proxies in proxy_config. | 0 | No |
| fallback_fetch_function | async (str) -> str or None | Async function called as last resort after all retries are exhausted. Takes URL, returns raw HTML. See Anti-Bot & Fallback. | No | |
| cache_mode | CacheMode or None | Controls how caching is handled (ENABLED, BYPASS, DISABLED, etc.). If None, typically defaults to ENABLED. | No | |
| session_id | str or None | Assign a unique ID to reuse a single browser session across multiple arun() calls. | No | |
| bypass_cache | bool | Deprecated. If True, acts like CacheMode.BYPASS. Use cache_mode instead. | False | No |
| disable_cache | bool | Deprecated. If True, acts like CacheMode.DISABLED. Use cache_mode instead. | False | No |
| no_cache_read | bool | Deprecated. If True, acts like CacheMode.WRITE_ONLY (writes cache but never reads). Use cache_mode instead. | False | No |
| no_cache_write | bool | Deprecated. If True, acts like CacheMode.READ_ONLY (reads cache but never writes). Use cache_mode instead. | False | No |
| shared_data | dict or None | Shared data to be passed between hooks and accessible across crawl operations. | No | |
| wait_until | str | Condition for navigation to "complete". Often "networkidle" or "domcontentloaded". | domcontentloaded | No |
| page_timeout | int | Timeout for page navigation or JS steps. Increase for slow sites. | 60000 | No |
| wait_for | str or None | Wait for a CSS ("css:selector") or JS ("js:() => bool") condition before content extraction. | No | |
| wait_for_timeout | int or None | Specific timeout in ms for the wait_for condition. If None, uses page_timeout. | No | |
| wait_for_images | bool | Wait for images to load before finishing. Slows down if you only want text. | False | No |
| delay_before_return_html | float | Additional pause (seconds) before final HTML is captured. Good for last-second updates. | 0.1 | No |
| check_robots_txt | bool | Whether to check and respect robots.txt rules before crawling. If True, caches robots.txt for efficiency. | False | No |
| mean_delay | float | If you call arun_many(), these define random delay intervals between crawls, helping avoid detection or rate limits. | 0.1 | No |
| max_range | float | If you call arun_many(), these define random delay intervals between crawls, helping avoid detection or rate limits. | 0.3 | No |
| semaphore_count | int | Max concurrency for arun_many(). Increase if you have resources for parallel crawls. | 5 | No |
| js_code | str or list[str] | JavaScript to run after wait_for and delay_before_return_html, on the fully-loaded page. E.g. "document.querySelector('button')?.click();". | No | |
| js_code_before_wait | str or list[str] | JavaScript to run before wait_for. Use for triggering loading that wait_for then checks (e.g. clicking a tab, then waiting for its content). | No | |
| c4a_script | str or list[str] | C4A script that compiles to JavaScript. Alternative to writing raw JS. | No | |
| js_only | bool | If True, indicates we're reusing an existing session and only applying JS. No full reload. | False | No |
| ignore_body_visibility | bool | Skip checking if <body> is visible. Usually best to keep True. | True | No |
| scan_full_page | bool | If True, auto-scroll the page to load dynamic content (infinite scroll). | False | No |
| scroll_delay | float | Delay between scroll steps when scanning the full page (scan_full_page=True) or capturing full-page screenshots. | 0.2 | No |
| max_scroll_steps | int or None | Maximum number of scroll steps during full page scan. If None, scrolls until entire page is loaded. | No | |
| process_iframes | bool | Inlines iframe content for single-page extraction. | False | No |
| flatten_shadow_dom | bool | Flattens Shadow DOM content into the light DOM before HTML capture. Resolves slots, strips shadow-scoped styles, and force-opens closed shadow roots. Essential for sites built with Web Components… | False | No |
| remove_overlay_elements | bool | Removes potential modals/popups blocking the main content. | False | No |
| remove_consent_popups | bool | Removes GDPR/cookie consent popups from known CMP providers (OneTrust, Cookiebot, TrustArc, Quantcast, Didomi, Sourcepoint, FundingChoices, etc.). Tries clicking "Accept All" first, then falls back… | False | No |
| simulate_user | bool | Simulate user interactions (mouse movements) to avoid bot detection. | False | No |
| override_navigator | bool | Override navigator properties in JS for stealth. | False | No |
| magic | bool | Automatic handling of popups/consent banners. Experimental. | False | No |
| adjust_viewport_to_content | bool | Resizes viewport to match page content height. | False | No |
| screenshot | bool | Capture a screenshot (base64) in result.screenshot. | False | No |
| screenshot_wait_for | float or None | Extra wait time before the screenshot. | No | |
| screenshot_height_threshold | int | If the page is taller than this, alternate screenshot strategies are used. | 20000 | No |
| force_viewport_screenshot | bool | If True, always captures a viewport-only screenshot regardless of page height. Faster and smaller than full-page screenshots. | False | No |
| bool | If True, returns a PDF in result.pdf. | False | No | |
| capture_mhtml | bool | If True, captures an MHTML snapshot of the page in result.mhtml. MHTML includes all page resources (CSS, images, etc.) in a single file. | False | No |
| image_description_min_word_threshold | int | Minimum words for an image's alt text or description to be considered valid. | 50 | No |
| image_score_threshold | int | Filter out low-scoring images. The crawler scores images by relevance (size, context, etc.). | 3 | No |
| exclude_external_images | bool | Exclude images from other domains. | False | No |
| exclude_all_images | bool | If True, excludes all images from processing (both internal and external). | False | No |
| table_score_threshold | int | Minimum score threshold for processing a table. Lower values include more tables. | 7 | No |
| table_extraction | TableExtractionStrategy | Strategy for table extraction. Defaults to DefaultTableExtraction with configured threshold. | DefaultTableExtraction | No |
| exclude_social_media_domains | list | A default list can be extended. Any link to these domains is removed from final output. | default list | No |
| exclude_external_links | bool | Removes all links pointing outside the current domain. | False | No |
| exclude_social_media_links | bool | Strips links specifically to social sites (like Facebook or Twitter). | False | No |
| exclude_domains | list | Provide a custom list of domains to exclude (like ["ads.com", "trackers.io"]). | [] | No |
| exclude_internal_links | bool | If True, excludes internal links from the results. | False | No |
| score_links | bool | If True, calculates intrinsic quality scores for all links using URL structure, text quality, and contextual metrics. | False | No |
| preserve_https_for_internal_links | bool | If True, preserves HTTPS scheme for internal links even when the server redirects to HTTP. Useful for security-conscious crawling. | False | No |
| verbose | bool | Prints logs detailing each step of crawling, interactions, or errors. | True | No |
| log_console | bool | Logs the page's JavaScript console output if you want deeper JS debugging. | False | No |
| capture_network_requests | bool | If True, captures network requests made by the page in result.captured_requests. | False | No |
| capture_console_messages | bool | If True, captures console messages from the page in result.console_messages. | False | No |
| method | str | HTTP method to use when using AsyncHTTPCrawlerStrategy (e.g., "GET", "POST"). | GET | No |
| stream | bool | If True, enables streaming mode for arun_many() to process URLs as they complete rather than waiting for all. | False | No |
| url | str or None | URL for this specific config. Not typically set directly but used internally for URL-specific configurations. | No | |
| user_agent | str or None | Custom User-Agent string for this crawl. Can override browser-level user agent. | No | |
| user_agent_mode | str or None | Set to "random" to randomize user agent. Can override browser-level setting. | No | |
| user_agent_generator_config | dict | Configuration for user agent generation when user_agent_mode="random". | {} | No |
| virtual_scroll_config | VirtualScrollConfig or dict | Configuration for handling virtualized scrolling on sites like Twitter/Instagram where content is replaced rather than appended. | No | |
| container_selector | str | CSS selector for the scrollable container (e.g., "#feed", ".timeline") | Yes | |
| scroll_count | int | Maximum number of scrolls to perform | 10 | No |
| scroll_by | str or int | Scroll amount: "container_height", "page_height", or pixels (e.g., 500) | container_height | No |
| wait_after_scroll | float | Time in seconds to wait after each scroll for new content to load | 0.5 | No |
| url_matcher | UrlMatcher | Pattern(s) to match URLs against. Can be: string (glob), function, or list of mixed types. None means match ALL URLs | No | |
| match_mode | MatchMode | How to combine multiple matchers in a list: MatchMode.OR (any match) or MatchMode.AND (all must match) | MatchMode.OR | No |
| deep_crawl_strategy | DeepCrawlStrategy or None | Strategy for deep/recursive crawling. Enables automatic link following and multi-level site crawling. | No | |
| link_preview_config | LinkPreviewConfig or dict or None | Configuration for link head extraction and scoring. Fetches and scores link metadata without full page loads. | No | |
| experimental | dict or None | Dictionary for experimental/beta features not yet integrated into main parameters. Use with caution. | No | |
| check_robots_txt | bool | When True, checks and respects robots.txt rules before crawling. Uses efficient caching with SQLite backend. | False | No |
| user_agent | str | User agent string to identify your crawler. Used for robots.txt checking when enabled. | No | |
| provider | str | Which LLM provider to use. | openai/gpt-4o-mini | No |
| api_token | str | API token to use for the given provider. Optional. When not provided explicitly, api_token will be read from environment variables based on provider. For example: If a gemini model is passed as… | No | |
| base_url | str | If your provider has a custom endpoint. | No | |
| backoff_base_delay | int | Seconds to wait before the first retry when the provider throttles a request. | 2 | No |
| backoff_max_attempts | int | Total tries (initial call + retries) before surfacing an error. | 3 | No |
| backoff_exponential_factor | int | Multiplier that increases the wait time for each retry (delay = base_delay * factor^attempt). | 2 | No |
Strategies - Crawl4AI Documentation (v0.9.x)
API reference for Crawl4AI extraction and chunking strategies, covering LLMExtractionStrategy, RegexExtractionStrategy, CosineStrategy, JsonCssExtractionStrategy, and various chunking strategies with…
Extraction Strategies
All extraction strategies inherit from the base ExtractionStrategy class and implement two key methods:
extract(url: str, html: str) -> List[Dict[str, Any]]- `run(url: str, sections:…
LLMExtractionStrategy
Used for extracting structured data using Language Models.
LLMExtractionStrategy(
# Required Parameters
provider: str = DEFAULT_PROVIDER, # LLM provider (e.g., "ollama/llama2")
api_token: Optional[str] = None, # API token
#…
RegexExtractionStrategy
Used for fast pattern-based extraction of common entities using regular expressions.
RegexExtractionStrategy(
# Pattern Configuration
pattern: IntFlag = RegexExtractionStrategy.Nothing, # Bit flags of built-in patterns to use
custom: Optional[Dict[str, str]] = None,…
CosineStrategy
Used for content similarity-based extraction and clustering.
CosineStrategy(
# Content Filtering
semantic_filter: str = None, # Topic/keyword filter
word_count_threshold: int = 10, # Minimum words per cluster
sim_threshold: float =…
JsonCssExtractionStrategy
Used for CSS selector-based structured data extraction.
JsonCssExtractionStrategy(
schema: Dict[str, Any], # Extraction schema
verbose: bool = False # Enable verbose logging
)
# Schema Structure
schema = {
"name": str, #…
Chunking Strategies
All chunking strategies inherit from ChunkingStrategy and implement the chunk(text: str) -> list method.
RegexChunking
Splits text based on regex patterns.
RegexChunking(
patterns: List[str] = None # Regex patterns for splitting
# Default: [r'\n\n']
)
SlidingWindowChunking
Creates overlapping chunks with a sliding window approach.
SlidingWindowChunking(
window_size: int = 100, # Window size in words
step: int = 50 # Step size between windows
)
OverlappingWindowChunking
Creates chunks with specified overlap.
OverlappingWindowChunking(
window_size: int = 1000, # Chunk size in words
overlap: int = 100 # Overlap size in words
)
Usage Examples
LLM Extraction
from pydantic import BaseModel
from crawl4ai import LLMExtractionStrategy
from crawl4ai import LLMConfig
# Define schema
class Article(BaseModel):
title: str
content: str
author: str
#…
Regex Extraction
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, RegexExtractionStrategy
# Method 1: Use built-in patterns
strategy = RegexExtractionStrategy(
pattern =…
CSS Extraction
from crawl4ai import JsonCssExtractionStrategy
# Define schema
schema = {
"name": "Product List",
"baseSelector": ".product-card",
"fields": [
{
"name": "title",…
Content Chunking
from crawl4ai.chunking_strategy import OverlappingWindowChunking
from crawl4ai import LLMConfig
# Create chunking strategy
chunker = OverlappingWindowChunking(
window_size=500, # 500 words per…
Best Practices
Choose the Right Strategy
- Use
RegexExtractionStrategyfor common data types like emails, phones, URLs, dates - Use
JsonCssExtractionStrategyfor well-structured HTML with consistent patterns - Use…
Strategy Selection Guide
Is the target data a common type (email/phone/date/URL)?
→ RegexExtractionStrategy
Does the page have consistent HTML structure?
→ JsonCssExtractionStrategy or JsonXPathExtractionStrategy
Is the…
Optimize Chunking
# For long documents
strategy = LLMExtractionStrategy(
chunk_token_threshold=2000, # Smaller chunks
overlap_rate=0.1 # 10% overlap
)
Combine Strategies for Best Performance
# First pass: Extract structure with CSS
css_strategy = JsonCssExtractionStrategy(product_schema)
css_result = await crawler.arun(url,…
Handle Errors
try:
result = await crawler.arun(
url="https://example.com",
extraction_strategy=strategy
)
if result.success:
content =…
Monitor Performance
strategy = CosineStrategy(
verbose=True, # Enable logging
word_count_threshold=20, # Filter short content
top_k=5 # Limit results
)
Cache Generated Patterns
# For RegexExtractionStrategy pattern generation
import json
from pathlib import Path
cache_dir = Path("./pattern_cache")
cache_dir.mkdir(exist_ok=True)
pattern_file = cache_dir /…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| provider | str | LLM provider (e.g., "ollama/llama2"). | DEFAULT_PROVIDER | No |
| api_token | Optional[str] | API token. | None | No |
| instruction | str | Custom extraction instruction. | None | No |
| schema | Dict | Pydantic model schema for structured data (LLMExtractionStrategy). | None | No |
| extraction_type | str | "block" or "schema". | "block" | No |
| chunk_token_threshold | int | Maximum tokens per chunk. | 4000 | No |
| overlap_rate | float | Overlap between chunks. | 0.1 | No |
| word_token_rate | float | Word to token conversion rate. | 0.75 | No |
| apply_chunking | bool | Enable/disable chunking. | True | No |
| base_url | str | Base URL for API. | None | No |
| extra_args | Dict | Additional provider arguments. | {} | No |
| verbose | bool | Enable verbose logging (LLMExtractionStrategy). | False | No |
| pattern | IntFlag | Bit flags of built-in patterns to use. | RegexExtractionStrategy.Nothing | No |
| custom | Optional[Dict[str, str]] | Custom pattern dictionary {label: regex}. | None | No |
| input_format | str | "html", "markdown", "text" or "fit_html". | "fit_html" | No |
| semantic_filter | str | Topic/keyword filter. | None | No |
| word_count_threshold | int | Minimum words per cluster. | 10 | No |
| sim_threshold | float | Similarity threshold. | 0.3 | No |
| max_dist | float | Maximum cluster distance. | 0.2 | No |
| linkage_method | str | Clustering method. | 'ward' | No |
| top_k | int | Top clusters to return. | 3 | No |
| model_name | str | Embedding model. | 'sentence-transformers/all-MiniLM-L6-v2' | No |
| verbose | bool | Enable verbose logging (CosineStrategy). | False | No |
| schema | Dict[str, Any] | Extraction schema (JsonCssExtractionStrategy). | Yes | |
| verbose | bool | Enable verbose logging (JsonCssExtractionStrategy). | False | No |
| patterns | List[str] | Regex patterns for splitting. Default: [r'\n\n'] | None | No |
| window_size | int | Window size in words (SlidingWindowChunking). | 100 | No |
| step | int | Step size between windows. | 50 | No |
| window_size | int | Chunk size in words (OverlappingWindowChunking). | 1000 | No |
| overlap | int | Overlap size in words. | 100 | No |
Crawl4AI Complete SDK Documentation
Comprehensive SDK reference for Crawl4AI, covering installation, quick start, core API (AsyncWebCrawler, arun, arun_many, CrawlResult), configuration, and extraction strategies.
Installation & Setup
Installation & Setup (2023 Edition)
1. Basic Installation
2. Initial Setup & Diagnostics
2.1 Run the Setup Command
- Performs OS-level checks (e.g., missing libs on Linux)
- Confirms…
pip install crawl4ai
crawl4ai-setup
crawl4ai-doctor
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(…
pip install crawl4ai[torch]
crawl4ai-setup
pip install crawl4ai[transformer]
crawl4ai-setup
pip install crawl4ai[all]
crawl4ai-setup
crawl4ai-download-models
docker pull unclecode/crawl4ai:basic
docker run -p 11235:11235 unclecode/crawl4ai:basic
Quick Start
Getting Started with Crawl4AI
- Run your first crawl using minimal configuration.
- Experiment with a simple CSS-based extraction strategy.
- Crawl a dynamic page that loads content…
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun("https://example.com")…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
async def main():
browser_conf = BrowserConfig(headless=True) # or False to see the browser…
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai.content_filter_strategy import PruningContentFilter
from crawl4ai.markdown_generation_strategy import…
from crawl4ai import JsonCssExtractionStrategy
from crawl4ai import LLMConfig
# Generate a schema (one-time cost)
html = "<div class='product'><h2>Gaming Laptop</h2><span…
import asyncio
import json
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def main():
schema = {
"name": "Example…
import os
import json
import asyncio
from pydantic import BaseModel, Field
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, LLMConfig
from crawl4ai import LLMExtractionStrategy
class…
import asyncio
from crawl4ai import AsyncWebCrawler, AdaptiveCrawler
async def adaptive_example():
async with AsyncWebCrawler() as crawler:
adaptive = AdaptiveCrawler(crawler)
#…
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def quick_parallel_example():
urls = [
"https://example.com/page1",…
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def…
Core API
AsyncWebCrawler
The AsyncWebCrawler is the core class for asynchronous web crawling in Crawl4AI. You typically create it once, optionally customize it with a BrowserConfig (e.g.,…
class AsyncWebCrawler:
def __init__(
self,
crawler_strategy: Optional[AsyncCrawlerStrategy] = None,
config: Optional[BrowserConfig] = None,
always_bypass_cache:…
from crawl4ai import AsyncWebCrawler, BrowserConfig
browser_cfg = BrowserConfig(
browser_type="chromium",
headless=True,
verbose=True
)
crawler = AsyncWebCrawler(config=browser_cfg)
async with AsyncWebCrawler(config=browser_cfg) as crawler:
result = await crawler.arun("https://example.com")
# The crawler automatically starts/closes resources
crawler = AsyncWebCrawler(config=browser_cfg)
await crawler.start()
result1 = await crawler.arun("https://example.com")
result2 = await crawler.arun("https://another.com")
await crawler.close()
async def arun(
url: str,
config: Optional[CrawlerRunConfig] = None,
# Legacy parameters for backward compatibility...
import asyncio
from crawl4ai import CrawlerRunConfig, CacheMode
run_cfg = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
css_selector="main.article",
word_count_threshold=10,…
async def arun_many(
urls: List[str],
config: Optional[CrawlerRunConfig] = None,
# Legacy parameters maintained for backwards compatibility...
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
import json
async def main():
# 1. Browser config…
run_cfg = CrawlerRunConfig(css_selector=".main-content", word_count_threshold=20)
result = await crawler.arun(url="...", config=run_cfg)
`arun()` Parameter Guide (New Approach)
In Crawl4AI’s latest configuration model, nearly all parameters that once went directly to arun() are now part of CrawlerRunConfig. When calling arun(), you provide:
Below is an…
await crawler.arun(
url="https://example.com",
config=my_run_config
)
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
run_config = CrawlerRunConfig(
verbose=True, # Detailed logging…
run_config = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS
)
run_config = CrawlerRunConfig(
word_count_threshold=10, # Ignore text blocks <10 words
only_text=False, # If True, tries to remove non-text elements
keep_data_attributes=False…
run_config = CrawlerRunConfig(
css_selector=".main-content", # Focus on .main-content region only
excluded_tags=["form", "nav"], # Remove entire tag blocks
remove_forms=True,…
run_config = CrawlerRunConfig(
exclude_external_links=True, # Remove external links from final content
exclude_social_media_links=True, # Remove links to known social sites…
run_config = CrawlerRunConfig(
exclude_external_images=True # Strip images from other domains
)
run_config = CrawlerRunConfig(
wait_for="css:.dynamic-content", # Wait for .dynamic-content
delay_before_return_html=2.0, # Wait 2s before capturing final HTML
page_timeout=60000,…
run_config = CrawlerRunConfig(
js_code=[
"window.scrollTo(0, document.body.scrollHeight);",
"document.querySelector('.load-more')?.click();"
],
js_only=False
)
run_config = CrawlerRunConfig(
magic=True,
simulate_user=True,
override_navigator=True
)
run_config = CrawlerRunConfig(
session_id="my_session123"
)
run_config = CrawlerRunConfig(
screenshot=True, # Grab a screenshot as base64
screenshot_wait_for=1.0, # Wait 1s before capturing
pdf=True, # Also…
run_config = CrawlerRunConfig(
extraction_strategy=my_css_or_llm_strategy
)
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy
async def main():
# Example schema
schema = {
"name":…
`arun_many(...)` Reference
Note: This function is very similar to
arun()but focused on concurrent or batch crawling. If you’re unfamiliar witharun()usage, please read that doc first, then…
async def arun_many(
urls: Union[List[str], List[Any]],
config: Optional[Union[CrawlerRunConfig, List[CrawlerRunConfig]]] = None,
dispatcher: Optional[BaseDispatcher] = None,
...
) ->…
# Minimal usage: The default dispatcher will be used
results = await crawler.arun_many(
urls=["https://site1.com", "https://site2.com"],
config=CrawlerRunConfig(stream=False) # Default…
config = CrawlerRunConfig(
stream=True, # Enable streaming mode
cache_mode=CacheMode.BYPASS
)
# Process results as they complete
async for result in await crawler.arun_many(…
dispatcher = MemoryAdaptiveDispatcher(
memory_threshold_percent=70.0,
max_session_permit=10
)
results = await crawler.arun_many(
urls=["https://site1.com", "https://site2.com",…
from crawl4ai import CrawlerRunConfig, MatchMode
from crawl4ai.processors.pdf import PDFContentScrapingStrategy
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
from…
`CrawlResult` Reference
The CrawlResult class encapsulates everything returned after a single crawl operation. It provides the raw or processed content, details on links and media, plus optional metadata (like…
class CrawlResult(BaseModel):
url: str
html: str
success: bool
cleaned_html: Optional[str] = None
fit_html: Optional[str] = None # Preprocessed HTML optimized for extraction…
print(result.url) # e.g., "https://example.com/"
if not result.success:
print(f"Crawl failed: {result.error_message}")
if result.status_code == 404:
print("Page not found!")
if not result.success:
print("Error:", result.error_message)
# If you used session_id="login_session" in CrawlerRunConfig, see it here:
print("Session:", result.session_id)
if result.response_headers:
print("Server:", result.response_headers.get("Server", "Unknown"))
if result.ssl_certificate:
print("Issuer:", result.ssl_certificate.issuer)
# Possibly large
print(len(result.html))
print(result.cleaned_html[:500]) # Show a snippet
if result.markdown:
md_res = result.markdown
print("Raw MD:", md_res.raw_markdown[:300])
print("Citations MD:", md_res.markdown_with_citations[:300])
print("References:",…
print(result.markdown.raw_markdown[:200])
print(result.markdown.fit_markdown)
print(result.markdown.fit_html)
images = result.media.get("images", [])
for img in images:
if img.get("score", 0) > 5:
print("High-value image:", img["src"])
for link in result.links["internal"]:
print(f"Internal link to {link['href']} with text {link['text']}")
if result.extracted_content:
data = json.loads(result.extracted_content)
print(data)
if result.downloaded_files:
for file_path in result.downloaded_files:
print("Downloaded:", file_path)
import base64
if result.screenshot:
with open("page.png", "wb") as f:
f.write(base64.b64decode(result.screenshot))
if result.pdf:
with open("page.pdf", "wb") as f:
f.write(result.pdf)
if result.mhtml:
with open("page.mhtml", "w", encoding="utf-8") as f:
f.write(result.mhtml)
if result.metadata:
print("Title:", result.metadata.get("title"))
print("Author:", result.metadata.get("author"))
# Example usage:
for result in results:
if result.success and result.dispatch_result:
dr = result.dispatch_result
print(f"URL: {result.url}, Task ID: {dr.task_id}")…
Parameters
| Name | Type | Description | Default | Required |
|---|---|---|---|---|
| crawler_strategy | Optional[AsyncCrawlerStrategy] | (Advanced) Provide a custom crawler strategy if needed. | None | No |
| config | Optional[BrowserConfig] | A BrowserConfig object specifying how the browser is set up. | None | No |
| always_bypass_cache | bool | (Deprecated) Use CrawlerRunConfig.cache_mode instead. | False | No |
| always_by_pass_cache | Optional[bool] | (Deprecated) Use CrawlerRunConfig.cache_mode instead. | None | No |
| base_directory | str | Folder for storing caches/logs (if relevant). | No | |
| thread_safe | bool | If True, attempts some concurrency safeguards. Usually False. | False | No |
| url | str | The URL to crawl. | Yes | |
| config (arun) | Optional[CrawlerRunConfig] | A CrawlerRunConfig object that sets up everything about a crawl—content filtering, caching, session reuse, JS code, screenshots, etc. | None | No |
| urls | Union[List[str], List[Any]] | A list of URLs (or tasks) to crawl. | Yes | |
| config (arun_many) | Optional[Union[CrawlerRunConfig, List[CrawlerRunConfig]]] | Either a single CrawlerRunConfig applying to all URLs, or a list of CrawlerRunConfig objects with url_matcher patterns. | None | No |
| dispatcher | Optional[BaseDispatcher] | A concurrency controller (e.g. MemoryAdaptiveDispatcher). | None | No |
Tools, CLI & Examples
🚀 Crawl4AI Interactive Apps
Overview of Crawl4AI's interactive demo apps, including the C4A-Script editor, LLM context builder, Chrome extension assistant, and upcoming tools for scraping experiments, prompt design, and…
🚀 Crawl4AI Interactive Apps
Welcome to the Crawl4AI Apps Hub - your gateway to interactive tools and demos that make web scraping more intuitive and powerful.
🛠️ Interactive Tools for Modern Web Scraping
Our apps are designed to make Crawl4AI more accessible and powerful. Whether you're learning browser automation, designing extraction strategies, or building complex scrapers, these tools provide…
🎯 Available Apps
🎨 C4A-Script Interactive Editor
Available
A visual, block-based programming environment for creating browser automation scripts. Perfect for beginners and experts alike!
- Drag-and-drop visual programming
- Real-time JavaScript…
🧠 LLM Context Builder
Available
Generate optimized context files for your favorite LLM when working with Crawl4AI. Get focused, relevant documentation based on your needs.
- Modular context generation
- Memory,…
🕸️ Web Scraping Playground
Coming Soon
Test your scraping strategies on real websites with instant feedback. See how different configurations affect your results.
- Live website testing
- Side-by-side result comparison -…
🔍 Crawl4AI Assistant (Chrome Extension)
Available
Visual schema builder Chrome extension - click on webpage elements to generate extraction schemas and Python code!
- Visual element selection
- Container & field selection modes
- Smart…
🧪 Extraction Lab
Coming Soon
Experiment with different extraction strategies and see how they perform on your content. Compare LLM vs CSS vs XPath approaches.
- Strategy comparison tools
- Performance benchmarks -…
🤖 AI Prompt Designer
Coming Soon
Craft and test prompts for LLM-based extraction. See how different prompts affect extraction quality and costs.
- Prompt templates library
- A/B testing interface
- Token usage…
📊 Crawl Monitor
Coming Soon
Real-time monitoring dashboard for your crawling operations. Track performance, debug issues, and optimize your scrapers.
- Real-time crawl statistics
- Error tracking and debugging -…
🚀 Why Use These Apps?
🎯 Accelerate Learning
Visual tools help you understand Crawl4AI's concepts faster than reading documentation alone.
💡 Reduce Development Time
Generate working code instantly instead of writing everything from scratch.
🔍 Improve Quality
Test and refine your approach before deploying to production.
🤝 Community Driven
These tools are built based on user feedback. Have an idea? Let us know!
📢 Stay Updated
Want to know when new apps are released?
- ⭐ Star us on GitHub to get notifications
- 🐦 Follow @unclecode for…
Developer Resources
Building your own tools with Crawl4AI? Check out our API Reference and Integration Guide for comprehensive documentation.
Build - Crawl4AI Documentation (v0.9.x)
This page contains a detailed prompt for an AI coding assistant to build an interactive HTML/JavaScript page that lets users select and combine crawl4ai LLM context Markdown files into a single…
Objective
Your task is to create an interactive HTML webpage with JavaScript functionality that allows users to select and combine different crawl4ai LLM context files into a single downloadable Markdown…
Core Functionality
- Display
crawl4aiComponents: The page will list all availablecrawl4aidocumentation components. - Select Context Types: For each component, users can select which types of context they…
Input/Assumptions
- Context Files Location: All individual context Markdown files are located on the server in a publicly accessible folder named
llmtxt/. - File Naming Convention: Files follow the pattern:…
Detailed UI/UX Requirements
- Main Page Structure:
- Header: "Crawl4AI Interactive LLM Context Builder"
- Introduction: Briefly explain the purpose of the tool (from the
USING_LLM_CONTEXTS.mdcontent you…
Final Output
- A single HTML file (e.g.,
interactive_context_builder.html). - Associated JavaScript code (can be inline within
<script>tags or in a separate.jsfile). - Associated CSS code (can be inline…
Supercharging Your AI Assistant: My Journey to Better LLM Contexts for `crawl4ai`
Explains the limitations of standard llm.txt files for providing AI coding assistants with context for crawl4ai, and introduces a multi-dimensional, modular context system using memory, reasoning,…
Introduction
When I started diving deep into using AI coding assistants with my own libraries, particularly crawl4ai, I quickly realized that the common approach to providing context via a simple llm.txt or…
My Frustration with Standard `llm.txt` Files
My experience with generic llm.txt files for complex libraries like crawl4ai revealed several pain points:
- Information Overload & Lost Focus: I found that when I threw a massive,…
Inspiration: Selective Inclusion & Multi-Dimensional Understanding
I've always admired how libraries like Lodash or jQuery (in its modular days) allowed developers to pick and choose only the parts they needed, resulting in smaller, more focused bundles. This idea…
Command Line Interface - Crawl4AI Documentation (v0.9.x)
This page provides a comprehensive guide to using the Crawl4AI CLI (`crwl`), covering installation, basic usage, configuration options (browser, crawler, extraction), advanced features like LLM Q&A…
Installation
The Crawl4AI CLI will be installed automatically when you install the library.
Basic Usage
The Crawl4AI CLI (crwl) provides a simple interface to the Crawl4AI library:
# Basic crawling
crwl https://example.com
# Get markdown output
crwl https://example.com -o markdown
# Verbose JSON output with cache bypass
crwl https://example.com -o json -v --bypass-cache
#…
Quick Example of Advanced Usage
If you clone the repository and run the following command, you will receive the content of the page in JSON format according to a JSON-CSS schema:
crwl "https://www.infoq.com/ai-ml-data-eng/" -e docs/examples/cli/extract_css.yml -s docs/examples/cli/css_schema.json -o json;
Copy
Configuration
Browser Configuration
Browser settings can be configured via YAML file or command line parameters:
# browser.yml
headless: true
viewport_width: 1280
user_agent_mode: "random"
verbose: true
ignore_https_errors: true
Copy
# Using config file
crwl https://example.com -B browser.yml
# Using direct parameters
crwl https://example.com -b "headless=true,viewport_width=1280,user_agent_mode=random"
Copy
Crawler Configuration
Control crawling behavior:
# crawler.yml
cache_mode: "bypass"
wait_until: "networkidle"
page_timeout: 30000
delay_before_return_html: 0.5
word_count_threshold: 100
scan_full_page: true
scroll_delay: 0.3
process_iframes:…
# Using config file
crwl https://example.com -C crawler.yml
# Using direct parameters
crwl https://example.com -c "css_selector=#main,delay_before_return_html=2,scan_full_page=true"
Copy
Extraction Configuration
Two types of extraction are supported:
- CSS/XPath-based extraction:
# extract_css.yml
type: "json-css"
params:
verbose: true
Copy
// css_schema.json
{
"name": "ArticleExtractor",
"baseSelector": ".article",
"fields": [
{
"name": "title",
"selector": "h1.title",
"type": "text"
},
{…
# extract_llm.yml
type: "llm"
provider: "openai/gpt-4"
instruction: "Extract all articles with their titles and links"
api_token: "your-token"
params:
temperature: 0.3
max_tokens: 1000
Copy
// llm_schema.json
{
"title": "Article",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "The title of the article"
},
"link": {…
Advanced Features
LLM Q&A
Ask questions about crawled content:
First-time setup:
- Prompts for LLM provider and API token
- Saves configuration in
~/.crawl4ai/global.yml - Supports various providers (openai/gpt-4,…
# Simple question
crwl https://example.com -q "What is the main topic discussed?"
# View content then ask questions
crwl https://example.com -o markdown # See content first
crwl https://example.com…
Structured Data Extraction
Extract structured data using CSS selectors:
Or using LLM-based extraction:
crwl https://example.com \
-e extract_css.yml \
-s css_schema.json \
-o json
Copy
crwl https://example.com \
-e extract_llm.yml \
-s llm_schema.json \
-o json
Copy
Content Filtering
Filter content for relevance:
# filter_bm25.yml
type: "bm25"
query: "target content"
threshold: 1.0
# filter_pruning.yml
type: "pruning"
query: "focus topic"
threshold: 0.48
Copy
crwl https://example.com -f filter_bm25.yml -o markdown-fit
Copy
Output Formats
all- Full crawl result including metadatajson- Extracted structured data (when using extraction)markdown/md- Raw markdown outputmarkdown-fit/md-fit- Filtered markdown…
Complete Examples
- Basic Extraction:
- Structured Data Extraction:
- LLM Extraction with Filtering:
- Interactive Q&A:
crwl https://example.com \
-B browser.yml \
-C crawler.yml \
-o json
Copy
crwl https://example.com \
-e extract_css.yml \
-s css_schema.json \
-o json \
-v
Copy
crwl https://example.com \
-B browser.yml \
-e extract_llm.yml \
-s llm_schema.json \
-f filter_bm25.yml \
-o json
Copy
# First crawl and view
crwl https://example.com -o markdown
# Then ask questions
crwl https://example.com -q "What are the main points?"
crwl https://example.com -q "Summarize the conclusions"
Copy
Best Practices & Tips
- Configuration Management :
- Keep common configurations in YAML files
- Use CLI parameters for quick overrides
- Store sensitive data (API tokens) in
~/.crawl4ai/global.yml-…
Recap
The Crawl4AI CLI provides:
- Flexible configuration via files and parameters
- Multiple extraction strategies (CSS, XPath, LLM)
- Content filtering and optimization
- Interactive Q&A capabilities -…
Code Examples
This page provides a comprehensive list of example scripts that demonstrate various features and capabilities of Crawl4AI, organized by category with links to code and guides.
Overview
This page provides a comprehensive list of example scripts that demonstrate various features and capabilities of Crawl4AI. Each example is designed to showcase specific functionality, making it…
Getting Started Examples
| Example | Description | Link |
|---|---|---|
| Hello World | A simple introductory example demonstrating basic usage of AsyncWebCrawler with JavaScript execution and content filtering. | … |
Proxies
| Example | Description | Link |
|---|---|---|
| NSTProxy | NSTProxy Seamlessly integrates with crawl4ai — no setup required. Access… |
Browser & Crawling Features
| Example | Description | Link |
|---|---|---|
| Built-in Browser | Demonstrates how to use the built-in browser capabilities. | [View… |
Advanced Crawling & Deep Crawling
| Example | Description | Link |
|---|---|---|
| Deep Crawling | An extensive tutorial on deep crawling capabilities, demonstrating BFS and BestFirst strategies, stream vs. non-stream… |
Extraction Strategies
| Example | Description | Link |
|---|---|---|
| Extraction Strategies | Demonstrates different extraction strategies with various input formats (markdown, HTML, fit_markdown) and JSON-based… |
E-commerce & Specialized Crawling
| Example | Description | Link |
|---|---|---|
| Amazon Product Extraction | Demonstrates how to extract structured product data from Amazon search results using CSS selectors. | [View… |
Anti-Bot & Stealth Features
| Example | Description | Link |
|---|---|---|
| Stealth Mode Quick Start | Five practical examples showing how to use stealth mode for bypassing basic bot detection. | [View… |
Customization & Security
| Example | Description | Link |
|---|---|---|
| Hooks | Illustrates how to use hooks at different stages of the crawling process for advanced customization. | [View… |
Docker & Deployment
| Example | Description | Link |
|---|---|---|
| Docker Config | Demonstrates how to create and use Docker configuration objects. | [View… |
Application Examples
| Example | Description | Link |
|---|---|---|
| Research Assistant | Demonstrates how to build a research assistant using Crawl4AI. | [View… |
Content Generation & Markdown
| Example | Description | Link |
|---|---|---|
| Content Source | Demonstrates how to work with different content sources in markdown generation. | [View… |
Running the Examples
To run any of these examples, you'll need to have Crawl4AI installed:
Then, you can run an example script like this:
For examples that require additional dependencies or environment variables,…
pip install crawl4ai
python -m docs.examples.hello_world
Contributing New Examples
If you've created an interesting example that demonstrates a unique use case or feature of Crawl4AI, we encourage you to contribute it to our examples collection. Please see our [contribution…
Migration & Contributing
Contributing Guide - Crawl4AI Documentation (v0.9.x)
Contribution guide for Crawl4AI explaining the branching strategy, contributor workflow, release process, and best practices for submitting pull requests.
Introduction
Welcome to the Crawl4AI project! As an open-source library for web crawling and AI integration, we value contributions from the community. This guide explains our branching strategy, how to…
Core Branches
-
main : The stable branch containing production-ready code. It's always identical to the latest released version and is tagged for releases. Do not submit PRs directly here.
-
develop : The…
Contributor Workflow
We encourage contributions of all kinds: bug fixes, new features, documentation improvements, tests, or even Docker enhancements. Follow these steps to contribute:
- Fork the Repository : Create…
git checkout develop
git checkout -b feature/your-feature-name # Or bugfix/your-bugfix-name
Lead Maintainer's Workflow (For Reference)
-
The lead maintainer (Unclecode) uses the
nextbranch for isolated experimental work. -
Features from
nextare periodically merged intodevelop(via rebase and merge) to keep everything in…
Release Process (High-Level Overview)
Releases happen bi-weekly to ship improvements regularly. As a contributor, your merged changes in develop will be included in the next release unless specified otherwise. Here's a summary of what…
Benefits of This Approach
-
Stability :
mainis always reliable for users. -
Collaboration : Fixed PR target (
develop) makes contributing straightforward. -
Isolation : Experimental work in
nextdoesn't…
Checklist for Contributors
Before submitting a PR:
-
[ ] Based on and targeting
develop. -
[ ] Tests pass (
pytest). -
[ ] Docs updated if needed (e.g., version refs in mkdocs.yml, Docker files).
-
[ ] No breaking…
Common Issues
-
Merge Conflicts : Rebase your branch on latest
developbefore PR. -
Docker Builds : Test multi-arch (amd64/arm64) locally if changing Dockerfile.
-
Version Consistency : Ensure any…
Communication
-
Open issues for discussions or bugs.
-
Join our Discord (link in README) for real-time help.
-
After releases, announcements go to GitHub, Discord, and social media.
Thanks for contributing to…
Migration Guide: Table Extraction v0.7.3
A migration guide for Crawl4AI v0.7.3 introducing the Table Extraction Strategy Pattern, highlighting new classes and options, full backward compatibility, migration scenarios, code organization…
Overview
Version 0.7.3 introduces the Table Extraction Strategy Pattern , providing a more flexible and extensible approach to table extraction while maintaining full backward compatibility.
What's New
Strategy Pattern Implementation
Table extraction now follows the same strategy pattern used throughout Crawl4AI:
- Consistent Architecture : Aligns with extraction, chunking, and markdown strategies
- Extensibility : Easy…
New Classes
from crawl4ai import (
TableExtractionStrategy, # Abstract base class
DefaultTableExtraction, # Current implementation (default)
NoTableExtraction # Explicitly disable…
Backward Compatibility
✅ All existing code continues to work without changes.
No Changes Required
If your code looks like this, it will continue to work:
# This still works exactly the same
config = CrawlerRunConfig(
table_score_threshold=7
)
result = await crawler.arun(url, config)
tables = result.tables # Same structure, same data
What Happens Behind the Scenes
When you don't specify a table_extraction strategy:
CrawlerRunConfigautomatically createsDefaultTableExtraction- It uses your
table_score_thresholdparameter - Tables are extracted…
New Capabilities
1. Explicit Strategy Configuration
You can now explicitly configure table extraction:
# New: Explicit control
strategy = DefaultTableExtraction(
table_score_threshold=7,
min_rows=2, # New: minimum row filter
min_cols=2, # New: minimum column…
2. Disable Table Extraction
Improve performance when tables aren't needed:
# New: Skip table extraction entirely
config = CrawlerRunConfig(
table_extraction=NoTableExtraction()
)
# No CPU cycles spent on table detection/extraction
3. Custom Extraction Strategies
Create specialized extractors:
class MyTableExtractor(TableExtractionStrategy):
def extract_tables(self, element, **kwargs):
# Custom extraction logic
return custom_tables
config = CrawlerRunConfig(…
Migration Scenarios
Scenario 1: Basic Usage (No Changes Needed)
Before (v0.7.2):
After (v0.7.3):
config = CrawlerRunConfig()
result = await crawler.arun(url, config)
for table in result.tables:
print(table['headers'])
# Exactly the same - no changes required
config = CrawlerRunConfig()
result = await crawler.arun(url, config)
for table in result.tables:
print(table['headers'])
Scenario 2: Custom Threshold (No Changes Needed)
Before (v0.7.2):
After (v0.7.3):
config = CrawlerRunConfig(
table_score_threshold=5
)
# Still works the same
config = CrawlerRunConfig(
table_score_threshold=5
)
# Or use new explicit approach for more control
strategy = DefaultTableExtraction(
table_score_threshold=5,…
Scenario 3: Advanced Filtering (New Feature)
Before (v0.7.2):
After (v0.7.3):
# Had to filter after extraction
config = CrawlerRunConfig(
table_score_threshold=5
)
result = await crawler.arun(url, config)
# Manual filtering
large_tables = [
t for t in result.tables…
# Filter during extraction (more efficient)
strategy = DefaultTableExtraction(
table_score_threshold=5,
min_rows=5,
min_cols=3
)
config = CrawlerRunConfig(…
Code Organization Changes
Module Structure
Before (v0.7.2):
After (v0.7.3):
crawl4ai/
content_scraping_strategy.py
- LXMLWebScrapingStrategy
- is_data_table() # Table detection
- extract_table_data() # Table extraction
crawl4ai/
content_scraping_strategy.py
- LXMLWebScrapingStrategy
# Table methods removed, uses strategy
table_extraction.py (NEW)
- TableExtractionStrategy # Base class
-…
Import Changes
New imports available (optional):
# These are now available but not required for existing code
from crawl4ai import (
TableExtractionStrategy,
DefaultTableExtraction,
NoTableExtraction
)
Performance Implications
No Performance Impact
For existing code, performance remains identical:
- Same extraction logic
- Same scoring algorithm
- Same processing time
Performance Improvements Available
New options for better performance:
# Skip tables entirely (faster)
config = CrawlerRunConfig(
table_extraction=NoTableExtraction()
)
# Process only specific areas (faster)
config = CrawlerRunConfig(…
Testing Your Migration
Verification Script
Run this to verify your extraction still works:
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
async def verify_extraction():
url = "your_url_here"
async with AsyncWebCrawler() as crawler:
# Test 1: Old…
Deprecation Notes
No Deprecations
- All existing parameters continue to work
table_score_thresholdinCrawlerRunConfigis still supported- No breaking changes
Internal Changes (Transparent to Users)
LXMLWebScrapingStrategy.is_data_table()- Moved toDefaultTableExtractionLXMLWebScrapingStrategy.extract_table_data()- Moved toDefaultTableExtraction
These methods were internal and…
Benefits of Upgrading
While not required, using the new pattern provides:
- Better Control : Filter tables during extraction, not after
- Performance Options : Skip extraction when not needed
- Extensibility…
Troubleshooting
Issue: Different Number of Tables
Cause : Threshold or filtering differences
Solution :
# Ensure same threshold
strategy = DefaultTableExtraction(
table_score_threshold=7, # Match your old setting
min_rows=0, # No filtering (default)
min_cols=0…
Issue: Import Errors
Cause : Using new classes without importing
Solution :
# Add imports if using new features
from crawl4ai import (
DefaultTableExtraction,
NoTableExtraction,
TableExtractionStrategy
)
Issue: Custom Strategy Not Working
Cause : Incorrect method signature
Solution :
class CustomExtractor(TableExtractionStrategy):
def extract_tables(self, element, **kwargs): # Correct signature
# Not: extract_tables(self, html)
# Not: extract(self, element)…
Getting Help
If you encounter issues:
- Check your
table_score_thresholdmatches previous settings - Verify imports if using new classes
- Enable verbose logging:
DefaultTableExtraction(verbose=True)-…
Summary
- ✅ Full backward compatibility - No code changes required
- ✅ Same results - Identical extraction behavior by default
- ✅ New options - Additional control when needed
- ✅ **Better…
WebScrapingStrategy Migration Guide
This guide explains the deprecation of BeautifulSoup-based WebScrapingStrategy in favor of LXMLWebScrapingStrategy, and confirms backward compatibility with no required changes.
Overview
Crawl4AI has simplified its content scraping architecture. The BeautifulSoup-based WebScrapingStrategy has been deprecated in favor of the faster LXML-based implementation. However, **no action is…
What Changed?
WebScrapingStrategyis now an alias forLXMLWebScrapingStrategy- The BeautifulSoup implementation has been removed (~1000 lines of redundant code)
- **
LXMLWebScrapingStrategy…
Backward Compatibility
Your existing code continues to work without any changes:
# This still works perfectly
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, WebScrapingStrategy
config = CrawlerRunConfig(
scraping_strategy=WebScrapingStrategy() # Works as before
)
Migration Options
You have three options:
Option 1: Do Nothing (Recommended)
Your code will continue to work. WebScrapingStrategy is permanently aliased to LXMLWebScrapingStrategy.
Option 2: Update Imports (Optional)
For clarity, you can update your imports:
# Old (still works)
from crawl4ai import WebScrapingStrategy
strategy = WebScrapingStrategy()
# New (more explicit)
from crawl4ai import LXMLWebScrapingStrategy
strategy = LXMLWebScrapingStrategy()
Option 3: Use Default Configuration
Since LXMLWebScrapingStrategy is the default, you can omit the strategy parameter:
# Simplest approach - uses LXMLWebScrapingStrategy by default
config = CrawlerRunConfig()
Type Hints
If you use type hints, both work:
from crawl4ai import WebScrapingStrategy, LXMLWebScrapingStrategy
def process_with_strategy(strategy: WebScrapingStrategy) -> None:
# Works with both WebScrapingStrategy and…
Subclassing
If you've subclassed WebScrapingStrategy, it continues to work:
class MyCustomStrategy(WebScrapingStrategy):
def __init__(self):
super().__init__()
# Your custom code
Performance Benefits
- 10-20x faster HTML parsing for large documents
- Lower memory usage
- Consistent behavior across all use cases
- Simplified maintenance and bug fixes
Summary
This change simplifies Crawl4AI's internals while maintaining 100% backward compatibility. Your existing code continues to work, and you get better performance automatically.