Build a concurrent web crawler
AnthropicSoftware Engineer · Technical ScreenLast updated
This question tests your ability to design a concurrent crawler that manages shared state across multiple threads, handles URL de-duplication, and prevents infinite cycles during graph traversal.
Problem Statement
Design a multi-threaded web crawler that fetches the content of web pages starting from a seed URL. The crawler should follow all discovered links recursively up to a specified depth max_depth.
Your implementation must be thread-safe and efficient, ensuring that:
- The same URL is not visited multiple times.
- The system handles potential network latency gracefully.
- The crawler respects a limit on the number of concurrent requests.
Implement the following interface:
class WebCrawler:
def __init__(self, max_depth, max_threads):
"""Initializes the crawler."""
def crawl(self, start_url):
"""Starts the crawling process from the given URL."""
def _fetch_page(self, url):
"""Helper to simulate fetching HTML. Returns a list of discovered URLs."""
Constraints
max_depthwill be a positive integer.max_threadswill be between 1 and 20.- Use a thread-safe approach to manage the set of visited URLs.
- You may use a thread pool or a semaphore to limit concurrency.
Examples
Example 1
Input: start_url = "http://a.com", max_depth = 1
Output: Returns a set of all URLs reachable from a.com that are at most 1 link away.
Example 2
Input: start_url = "http://a.com", max_threads = 2
Output: Crawler should process multiple URLs in parallel without redundant visits.
Hints
Hint 1
Use a thread-safe Set (e.g., protected by a Lock or using ConcurrentSet) to track visited URLs and avoid redundant work.
Hint 2
Utilize a ThreadPoolExecutor to manage the worker threads and a task queue to hold discovered URLs that need to be processed.
Hint 3
To handle depth tracking, store the current depth alongside the URL in the task queue or use a level-order traversal approach.
Follow-up questions
- How would you modify your design to be distributed across multiple physical machines instead of multiple threads on one machine?
- How can you add 'politeness' to your crawler (e.g., ensuring you don't overwhelm a single domain with requests)?
- What changes would be required to persist the crawler's state so it can resume after a crash?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.