Scenario
Build a resilient, asynchronous HTTP scraper capable of collecting data from hundreds of remote endpoints concurrently while respecting server rate limits and handling transient network failures.
Requirements
- Concurrency Control: Use
aiohttp.ClientSessionandasyncio.Semaphoreto enforce a hard concurrency ceiling (e.g., max 20 simultaneous sockets). - Resilience & Retries: Implement an async retry decorator with exponential backoff and jitter for
5xxresponses and network timeouts. Respect429 Too Many Requestsheaders. - Structured Metrics: Emit JSON-formatted logs for every request including URL, HTTP status, latency milliseconds, and bytes received.
- Graceful Shutdown: Intercept
SIGINT/SIGTERMto allow in-flight requests to finish cleanly and close connections.
Starter Code Layout
build-2-async-scraper/
├── src/
│ └── scraper/
│ ├── __init__.py
│ ├── client.py # aiohttp session with Semaphore
│ ├── retry.py # Exponential backoff decorator
│ └── runner.py # Main event loop runner
└── tests/
└── test_scraper.py # aioresponses mock tests
Verification Command
pytest tests/test_scraper.py -v
python -m scraper.runner --urls sample_urls.txt --concurrency 10
100-Point Evaluation Rubric
- AsyncIO & Semaphore Mechanics (30 pts): Concurrency bounded strictly by semaphore; no unbounded socket creation.
- Retry & Error Isolation (25 pts): Backoff calculation correct; transient errors retried; fatal errors logged without crashing.
- Metrics & Logging (25 pts): Structured JSON logs with p50/p95 throughput summary.
- Clean Teardown (20 pts): Session and event loop cleanly closed on exit.
Grading rubric
Your submission passes when all four hold:
- Works: Concurrency never exceeds the cap; 5xx/429s retried with backoff; JSON logs carry URL/status/latency/bytes; SIGINT shuts down cleanly.
- Tested: a pytest suite covers the happy path plus one failure mode, and all tests pass.
- Readable: a README explains setup, the two most important commands, and one design decision you made.
- Production habits: structured logging on failures and no hardcoded secrets/paths (env or config file).
Submission contract
Submit a public repo URL containing source, tests, README, and requirements.txt (or pyproject.toml). A reviewer runs your tests and two commands from your README; if either fails on a clean machine, the submission is returned with notes.