fix(deep_crawling): stop BFS batch mode from re-crawling a self-linking start URL#2073
Open
chuenchen309 wants to merge 1 commit into
Open
Conversation
…ng start URL BFSDeepCrawlStrategy._arun_batch() processed each BFS level's URLs without first adding them to the `visited` set, unlike its sibling _arun_stream(), which does `visited.update(urls)` right after computing the level's URL list (bfs_strategy.py, _arun_stream). Because of that asymmetry, in batch mode the start URL never entered `visited` before link_discovery() scanned the start page's own outgoing links. link_discovery() only adds a URL to `visited` when it *accepts* it as a newly discovered link (visited.add(base_url) in the "collect valid links" loop) - the start URL is seeded directly into current_level and never goes through that path, so it was never marked visited on its own. Concrete failure: crawl any site whose homepage contains a link back to itself - a "Home"/logo nav link, a canonical self-link, or even a bare href="#" (which normalize_url_for_deep_crawl reduces to the base URL after stripping the fragment). With BFSDeepCrawlStrategy(max_depth=2)._arun_batch(...), that self-link is treated as a brand-new, unvisited URL: it gets appended to next_level and depths[start_url] is overwritten from 0 to 1, so the start page is crawled a second time at depth 1 with a corrupted depth/parent_url and an inflated _pages_crawled count. Stream mode does not have this bug - _arun_stream() already seeds `visited` correctly - so this was a batch-mode-only regression relative to the documented "one entry per crawled URL" behavior. CrawlerRunConfig.stream defaults to False, so _arun_batch is the default deep-crawl code path, not an edge case. Fix: add the same `visited.update(urls)` call to _arun_batch that _arun_stream already has, restoring parity between the two modes. Added tests/deep_crawling/test_bfs_batch_self_link.py using this repo's existing MagicMock-based crawler/config test double pattern (see tests/deep_crawling/test_deep_crawl_cancellation.py) to verify: the start URL is only crawled once when it self-links, its recorded depth stays 0, and batch/stream modes now agree on the resulting URL set for the same site graph. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
BFSDeepCrawlStrategy._arun_batch()processed each BFS level's URLs without first adding them to thevisitedset, unlike its sibling_arun_stream(), which doesvisited.update(urls)right after computing the level's URL list. Because of that asymmetry, in batch mode the start URL never enteredvisitedbeforelink_discovery()scanned the start page's own outgoing links —link_discovery()only adds a URL tovisitedwhen it accepts it as a newly discovered link; the start URL is seeded directly intocurrent_leveland never goes through that path.Concrete failure: crawl any site whose homepage links back to itself (a "Home"/logo nav link, a canonical self-link, or a bare
href="#", whichnormalize_url_for_deep_crawlreduces to the base URL after stripping the fragment). WithBFSDeepCrawlStrategy(max_depth=2)._arun_batch(...), that self-link is treated as brand-new and unvisited: it gets appended tonext_level, anddepths[start_url]is overwritten from0to1— the start page gets crawled a second time at depth 1 with a corrupted depth/parent_url and an inflated_pages_crawledcount._arun_stream()does not have this bug (it already seedsvisitedcorrectly), so this is a batch-mode-only regression relative to the documented "one entry per crawled URL" behavior.CrawlerRunConfig.streamdefaults toFalse, so_arun_batchis the default deep-crawl code path, not an edge case.Fixes #123: n/a — found via code review, no existing issue filed for this specific bug.
List of files changed and why
crawl4ai/deep_crawling/bfs_strategy.py— add the samevisited.update(urls)call to_arun_batchthat_arun_streamalready has, restoring parity between the two modes.tests/deep_crawling/test_bfs_batch_self_link.py(new) — regression tests using this repo's existing MagicMock-based crawler/config test double pattern (seetests/deep_crawling/test_deep_crawl_cancellation.py).How Has This Been Tested?
0, and batch/stream modes now agree on the resulting URL set for the same site graph.bfs_strategy.pyreproduces all 3 failures with the exact predicted symptoms (start URL crawled twice, depth overwritten, batch/stream URL-set mismatch); reapplying the fix passes.tests/deep_crawling/(72 tests) andtests/unit/+tests/deep_crawling/combined (120 tests): all pass, no regressions.pyproject.toml, noMakefile/.pre-commit-config.yaml); confirmed changed files compile cleanly withpython -m py_compile.Checklist:
AI-Generated disclosure
Found via an AI-assisted code review pass (Claude Code) over
crawl4ai/deep_crawling/. I personally traced thevisited-set asymmetry between_arun_batch/_arun_stream, confirmed the concrete self-link failure scenario, verified the fix, and ran the relevant test suites before submitting.