Apidog

All-in-one Collaborative API Development Platform

API Design

API Documentation

API Debugging

API Mocking

API Automated Testing

Crawl4AI Tutorial: A Beginner's Guide

Mark Ponomarev

Mark Ponomarev

Updated on April 30, 2025

In the age of AI, accessing and processing web data efficiently is crucial. Crawl4AI emerges as a powerful, open-source web crawler and scraper meticulously engineered for developers working with Large Language Models (LLMs), AI agents, and modern data pipelines. This tutorial provides a deep dive into Crawl4AI, covering everything from installation to advanced crawling techniques.

💡
Want a great API Testing tool that generates beautiful API Documentation?

Want an integrated, All-in-One platform for your Developer Team to work together with maximum productivity?

Apidog delivers all your demans, and replaces Postman at a much more affordable price!
button

Why Choose Crawl4AI for Your Projects?

Crawl4AI is more than just a standard web scraper. It's designed from the ground up to be LLM-friendly. This means it focuses on:

  • Clean Markdown Generation: Producing well-structured, concise Markdown optimized for Retrieval-Augmented Generation (RAG) systems and model fine-tuning by removing boilerplate and noise.
  • Structured Data Extraction: Enabling the extraction of specific data points into formats like JSON using either traditional methods (CSS selectors, XPath) or leveraging LLMs for more complex, semantic extraction tasks.
  • High Performance: Utilizing Python's asyncio library and the powerful Playwright browser automation framework for fast, asynchronous crawling.
  • Advanced Browser Control: Offering fine-grained control over the browser instance, including JavaScript execution, handling dynamic content, managing sessions (cookies, local storage), using proxies, and simulating different user environments (user agents, geolocations).
  • Open Source & Flexibility: Being fully open-source (Apache 2.0 with attribution) with no reliance on external API keys or paid services. It offers deployment flexibility via Docker or direct pip installation.

Crawl4AI aims to democratize data access, empowering developers to gather and shape web data with speed and efficiency.


Installing and Setting Up Crawl4AI

Getting Crawl4AI running is straightforward, offering both pip and Docker options.

Method 1: Pip Installation (Recommended for Library Usage)

Install the Package: Open your terminal and run:

# Install the latest stable version
pip install -U crawl4ai

# Or, install the latest pre-release (for cutting-edge features)
# pip install crawl4ai --pre

Run Post-Installation Setup: This crucial step installs the necessary Playwright browser binaries (Chromium by default):

crawl4ai-setup

Verify: Check your setup using the diagnostic tool:

crawl4ai-doctor

Troubleshooting: If crawl4ai-setup encounters issues, manually install the browser dependencies:

python -m playwright install --with-deps chromium

Method 2: Docker Deployment (Ideal for API Service)

Pull the Image: Get the official Docker image (check GitHub for the latest tag):

# Example tag, replace if necessary
docker pull unclecode/crawl4ai:latest

Run the Container: Start the Crawl4AI service, exposing its API (default port 11235):

docker run -d -p 11235:11235 --name crawl4ai --shm-size=1g unclecode/crawl4ai:latest

This runs Crawl4AI with a FastAPI backend, ready to accept crawl requests via HTTP. You can access an interactive API playground at http://localhost:11235/playground.


How to Execute Your First Crawl with Crawl4AI

Crawl4AI makes basic crawling incredibly simple using the AsyncWebCrawler.

import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode

async def run_basic_crawl():
    # --- Basic Example ---
    print("--- Running Basic Crawl ---")
    # Use 'async with' for automatic browser startup and shutdown
    async with AsyncWebCrawler() as crawler:
        # The arun() method performs the crawl for a single URL
        # It returns a CrawlResult object
        result = await crawler.arun(url="https://example.com")

        if result and result.success:
            # Access the generated Markdown (usually 'fit_markdown')
            print("Crawl Successful!")
            # result.markdown provides both raw and filtered markdown
            print(f"Fit Markdown (first 300 chars): {result.markdown.fit_markdown[:300]}...")
        else:
            print(f"Crawl Failed: {result.error_message}")

    # --- Example with Basic Configuration ---
    print("\n--- Running Crawl with Basic Configuration ---")
    # Configure browser behavior (e.g., run headful for debugging)
    browser_conf = BrowserConfig(headless=True) # Set to False to see the browser window

    # Configure run-specific settings (e.g., bypass cache)
    # CacheMode.ENABLED (default), CacheMode.DISABLED, CacheMode.BYPASS
    run_conf = CrawlerRunConfig(cache_mode=CacheMode.BYPASS)

    async with AsyncWebCrawler(config=browser_conf) as crawler:
        result = await crawler.arun(
            url="https://crawl4ai.com/", # Crawl the official site
            config=run_conf # Apply the run configuration
        )
        if result and result.success:
            print("Crawl Successful!")
            print(f"Fit Markdown Word Count: {result.markdown.word_count}")
            print(f"URL Crawled: {result.url}")
        else:
            print(f"Crawl Failed: {result.error_message}")

if __name__ == "__main__":
    asyncio.run(run_basic_crawl())

Key Crawl4AI Concepts:

  • AsyncWebCrawler: The main class for initiating crawls. Using async with ensures the browser is properly managed.
  • arun(url, config=None): The core asynchronous method to crawl a single URL.
  • BrowserConfig: Controls browser-level settings (headless, user agent, proxies). Passed during AsyncWebCrawler initialization.
  • CrawlerRunConfig: Controls settings for a specific crawl job (caching, extraction strategies, timeouts, JavaScript execution). Passed to the arun method.
  • CacheMode: Determines how Crawl4AI interacts with its cache (ENABLED, DISABLED, BYPASS). BYPASS is useful for ensuring fresh data during development.

How Does the Crawl4AI CrawlResult Object Work?

Every successful arun or arun_many call returns one or more CrawlResult objects, which encapsulate all information gathered during the crawl.

The CrawlResult object contains various attributes, including:

  • url: The final URL crawled (after redirects).
  • success: Boolean indicating if the crawl was successful.
  • error_message: Contains error details if success is False.
  • status_code: HTTP status code of the response.
  • markdown: An object containing Markdown versions (raw_markdown, fit_markdown, word_count).
  • html: The raw HTML content of the page.
  • text: Plain text content extracted from the page.
  • extracted_content: Stores the result from any configured extraction strategy (e.g., JSON string).
  • links: A list of links found on the page (internal, external).
  • media: Information about extracted media (images, tables, etc.).
  • metadata: Page metadata (title, description, etc.).
  • cookies: Browser cookies after the crawl.
  • screenshot_path: Path to the screenshot if taken.
  • network_log_path: Path to the network HAR file if captured.
  • console_log_path: Path to the console log file if captured.

Inspecting this object is key to accessing the specific data you need from a Crawl4AI crawl.


How to Generate AI-Ready Markdown with Crawl4AI

A core strength of Crawl4AI is its ability to generate clean Markdown suitable for LLMs.

The result.markdown attribute holds:

  • result.markdown.raw_markdown: The direct, unfiltered conversion of the page's primary content area to Markdown.
  • result.markdown.fit_markdown: A filtered version of the Markdown. This is often the most useful for LLMs, as Crawl4AI applies heuristic filters (like PruningContentFilter or BM25ContentFilter) to remove common web clutter (menus, ads, footers, sidebars).
  • result.markdown.word_count: The word count of the fit_markdown.

You can customize the filtering process in Crawl4AI:

import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
# Import specific strategies for customization
from crawl4ai.content_filter_strategy import PruningContentFilter
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator

async def run_custom_markdown_crawl():
    print("\n--- Running Crawl with Custom Markdown Filtering ---")

    # Configure a Markdown generator with a specific content filter
    # PruningContentFilter removes elements based on word count thresholds
    markdown_generator_with_filter = DefaultMarkdownGenerator(
        content_filter=PruningContentFilter(
            threshold=0.48, # Adjust threshold (0 to 1) for strictness
            threshold_type="fixed" # 'fixed' or 'relative'
            )
    )

    # Apply this generator in the run configuration
    run_conf = CrawlerRunConfig(
        cache_mode=CacheMode.BYPASS,
        markdown_generator=markdown_generator_with_filter
    )

    async with AsyncWebCrawler() as crawler:
        result = await crawler.arun(
            url="https://www.nbcnews.com/business", # A news site often has clutter
            config=run_conf
        )
        if result and result.success:
            print("Crawl Successful!")
            print(f"Raw Markdown Length: {len(result.markdown.raw_markdown)}")
            print(f"Fit Markdown Length: {len(result.markdown.fit_markdown)}") # Usually shorter
            # Compare raw_markdown and fit_markdown to see the filter's effect
        else:
            print(f"Crawl Failed: {result.error_message}")

if __name__ == "__main__":
    asyncio.run(run_custom_markdown_crawl())

By tuning the content_filter within the markdown_generator, you control how aggressively Crawl4AI cleans the content before producing fit_markdown.


How to Use Crawl4AI Deep Crawling

Crawl4AI isn't limited to single pages. It can perform deep crawls, navigating through a website by following links.

Use the adeep_crawl method (or the crwl CLI's --deep-crawl flag):

import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode

async def run_deep_crawl():
    print("\n--- Running Deep Crawl ---")
    # Configuration can be applied globally or per-run as usual
    run_conf = CrawlerRunConfig(cache_mode=CacheMode.ENABLED)

    async with AsyncWebCrawler() as crawler:
        # adeep_crawl returns an async generator, yielding results as pages finish
        crawl_generator = await crawler.adeep_crawl(
            start_url="https://docs.crawl4ai.com/", # Starting point
            strategy="bfs", # 'bfs' (Breadth-First), 'dfs' (Depth-First), 'bestfirst'
            max_depth=2,    # Limit how many links deep to follow
            max_pages=10,   # Limit the total number of pages to crawl
            config=run_conf
        )

        # Process results as they come in
        pages_crawled = 0
        async for result in crawl_generator:
            if result.success:
                print(f"[OK] Crawled: {result.url} (Depth: {result.depth}, Fit Markdown Length: {len(result.markdown.fit_markdown)})")
                pages_crawled += 1
            else:
                print(f"[FAIL] URL: {result.url}, Error: {result.error_message}")

        print(f"\nDeep crawl finished. Total pages successfully crawled: {pages_crawled}")

if __name__ == "__main__":
    asyncio.run(run_deep_crawl())

Crawl4AI Deep Crawl Parameters:

  • start_url: The initial URL to begin crawling from.
  • strategy: How to discover and prioritize links (bfs, dfs, bestfirst).
  • max_depth: Maximum link distance from the start_url.
  • max_pages: Maximum total number of pages to crawl in this job.
  • include_patterns, exclude_patterns: Use regex patterns to filter which URLs are followed.

How to Handel Dynamic Content and Interactions with Crawl4AI

Modern websites heavily rely on JavaScript to load content. Crawl4AI handles this through Playwright's capabilities.

You can execute arbitrary JavaScript or wait for specific conditions using CrawlerRunConfig:

import asyncio
import json
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy # For example

async def crawl_dynamic_page():
    print("\n--- Crawling Dynamic Page with JS Interaction ---")

    # Example schema for CSS extraction (adapt to the target site)
    schema = { "items": { "selector": "div.product-item", "type": "list", "fields": { "title": "h2", "price": ".price" } } }
    css_extractor = JsonCssExtractionStrategy(schema)

    # JavaScript to execute on the page (e.g., click a 'Load More' button)
    # Note: Selector needs to match the target website
    js_to_run = """
    (async () => {
        const loadMoreButton = document.querySelector('button#load-more');
        if (loadMoreButton) {
            console.log('Clicking load more button...');
            loadMoreButton.click();
            // Wait a bit for content to potentially load after click
            await new Promise(resolve => setTimeout(resolve, 2000));
            console.log('Waited after click.');
        } else {
            console.log('Load more button not found.');
        }
    })();
    """

    run_conf = CrawlerRunConfig(
        cache_mode=CacheMode.BYPASS,
        js_code=[js_to_run], # List of JS snippets to execute
        wait_for_timeout=3000, # Wait 3 seconds after initial load AND after JS execution
        # wait_for_selector="div.newly-loaded-content", # Or wait for a specific element
        extraction_strategy=css_extractor, # Extract data after JS runs
        output_formats=['markdown', 'extracted_content']
    )

    # Ensure JS is enabled in BrowserConfig (it is by default)
    browser_conf = BrowserConfig(headless=True, java_script_enabled=True)

    async with AsyncWebCrawler(config=browser_conf) as crawler:
        result = await crawler.arun(
            url="URL_OF_DYNAMIC_PAGE_HERE", # Replace with actual URL
            config=run_conf
        )

        if result and result.success:
            print("Dynamic page crawl successful!")
            print(f"Fit Markdown Length: {len(result.markdown.fit_markdown)}")
            if result.extracted_content:
                try:
                    extracted_data = json.loads(result.extracted_content)
                    print(f"Extracted Content Preview: {json.dumps(extracted_data, indent=2)[:500]}...")
                except json.JSONDecodeError:
                    print(f"Extracted Content (non-JSON): {result.extracted_content[:500]}...")
        else:
            print(f"Crawl Failed: {result.error_message}")


if __name__ == "__main__":
    # Replace with an actual URL that loads content dynamically for testing
    # asyncio.run(crawl_dynamic_page())
    print("Please replace 'URL_OF_DYNAMIC_PAGE_HERE' and uncomment the line above to run the dynamic example.")

Key Crawl4AI Interaction Parameters in CrawlerRunConfig:

  • js_code: A list of JavaScript strings to execute in the page context.
  • wait_for_timeout: Milliseconds to wait after page load and after JS execution.
  • wait_for_selector: A CSS selector to wait for before considering the page loaded/interaction complete.
  • page_interaction_hooks: More advanced hooks for complex interactions.

Crawl4AI Conclusion

Crawl4AI provides a comprehensive, Pythonic, and AI-centric solution for web crawling and scraping. Its focus on clean Markdown generation, flexible structured data extraction (both CSS and LLM based), robust handling of dynamic content, and efficient asynchronous operation makes it an excellent choice for projects involving RAG, LLM fine-tuning, or any task requiring structured information from the web. By leveraging its clear API, configuration options (BrowserConfig, CrawlerRunConfig), and detailed CrawlResult object, developers can build sophisticated and efficient data-gathering workflows.

💡
Want a great API Testing tool that generates beautiful API Documentation?

Want an integrated, All-in-One platform for your Developer Team to work together with maximum productivity?

Apidog delivers all your demans, and replaces Postman at a much more affordable price!
button
How to Use Resend API (A Beginners Guide)Viewpoint

How to Use Resend API (A Beginners Guide)

Email is a critical component of modern web applications, used for everything from user onboarding and notifications to password resets and marketing campaigns. However, building and managing a reliable email sending infrastructure can be complex and time-consuming. This is where email API services like Resend come in. Resend offers a developer-friendly platform designed to simplify the process of sending transactional and marketing emails. It provides robust APIs, detailed analytics, and excel

Mark Ponomarev

April 29, 2025

How to Run Qwen 3 Locally with Ollama & VLLMViewpoint

How to Run Qwen 3 Locally with Ollama & VLLM

The landscape of large language models (LLMs) is evolving at breakneck speed. Models are becoming more powerful, capable, and, importantly, more accessible. The Qwen team recently unveiled Qwen3, their latest generation of LLMs, boasting impressive performance across various benchmarks, including coding, math, and general reasoning. With flagship models like the Mixture-of-Experts (MoE) Qwen3-235B-A22B rivaling established giants and even smaller dense models like Qwen3-4B competing with previou

Ashley Innocent

April 29, 2025

How to Use Deepseek V3 with Cursor for FreeViewpoint

How to Use Deepseek V3 with Cursor for Free

Learn to use Deepseek V3 for FREE with Cursor in this step-by-step beginner's guide! Code a Python factorial function with AI. My takes: fast and cost-effective!

Ashley Goolam

April 29, 2025