Adaptive & Deep Crawling
You will send a focused crawler into a site, let it decide which links matter, and stop it before it spirals forever.
Concept
AdaptiveCrawler combines a seed URL, a query, and an LLM judge to decide which discovered links are worth following. It is useful when the target site has no predictable list page but the content you want follows a theme.
BFSDeepCrawlStrategy gives you breadth-first exploration with configurable depth and stop conditions. Use it when you know the structure and want to enumerate pages systematically.
Both strategies need guardrails: a hard max_pages limit, rate limiting, and saved state. Save state to disk so a long crawl can resume after an interruption.
Use cases
- Discover every documentation page related to a specific feature.
- Enumerate a blog archive to a fixed depth without hitting every tag page.
- Resume a long crawl from a saved state file after a restart.
Video walkthrough
Code example
from crawl4ai import AdaptiveCrawler, AdaptiveConfig
config = AdaptiveConfig(
confidence_threshold=0.85,
max_pages=30,
top_k_links=3,
min_gain_threshold=0.05,
save_state=True,
state_path="crawl_state.json",
)
async def research(url: str, query: str):
async with AsyncWebCrawler() as crawler:
adaptive = AdaptiveCrawler(crawler, config=config)
result = await adaptive.digest(url, query)
print(result)
from crawl4ai.adaptive_crawler import CrawlStrategy, CrawlState
from typing import List
class DomainSpecificStrategy(CrawlStrategy):
def calculate_coverage(self, state: CrawlState) -> float:
# Return coverage score between 0 and 1
return 0.0
def calculate_consistency(self, state: CrawlState) -> float:
return 1.0
def rank_links(self, links: List, state: CrawlState) -> List:
# Sort links by domain relevance
return sorted(links, key=lambda l: l.url.count("/docs/"), reverse=True)
Practice lab
Practice lab
Objective: Configure an adaptive crawl with a hard page limit and a save-state file.
Steps
- Import
AdaptiveCrawlerandAdaptiveConfig. - Set
max_pages=10,top_k_links=2, andsave_state=Truewith astate_path. - Start the crawl from one seed URL and a focused query.
- Check that the state file is created.
- Print how many pages were visited.
Success criteria
Expected output
Hints
- Start with a small
max_pagesvalue to keep the lab fast. - Use a focused query; broad queries produce larger crawl graphs.
Solution
import asyncio
from crawl4ai import AsyncWebCrawler, AdaptiveCrawler, AdaptiveConfig
async def main():
config = AdaptiveConfig(
max_pages=5,
top_k_links=2,
save_state=True,
state_path="adaptive_state.json",
)
async with AsyncWebCrawler() as crawler:
adaptive = AdaptiveCrawler(crawler, config=config)
result = await adaptive.digest(
url="https://example.com/docs",
query="async crawler configuration",
)
print(f"Visited {len(result)} pages")
asyncio.run(main())
Common mistakes
confidence_threshold and a low min_gain_threshold, causing very long crawls.