HTML scrape
CodeChef has no public JSON stats API that this service trusts. The source of truth is the public profile HTML plus two XHR endpoints the site already uses. End-to-end: browser hits this FastAPI app, the app GETs CodeChef, BeautifulSoup plus regex pull fields, mapper wraps the shared envelope.
Playground: playground. Sample handle tourist. Timeout CODECHEF_REQUEST_TIMEOUT default 120s. Chrome User-Agent.
fetch_codechef_profile loads https://www.codechef.com/users/{handle} and parse_codechef_profile pulls inline JS plus DOM.
JS blobs:
var all_rating = ...;contest seriesvar userDailySubmissionsStats = ...;heatmap, either a dict{date: value}or a list
DOM: .rating-number, .rating stars, country, ranks, “Total Problems Solved”. Dates like "2024-8-6" (unpadded). Stars "unrated" are dropped from rank.
Topics are not on the profile. The scraper pages https://www.codechef.com/recent/user?user_handle={handle}&page=N (XHR JSON wrapping HTML content), caps at 40 pages, 10 concurrent, then GET https://www.codechef.com/api/contests/PRACTICE/problems/{code} for computed_tags else user_tags.
Badges are empty. CodeChef does not expose a public badge list that survived the last HTML change. Mapper comment says so.
byDifficulty is {}. Stars become contests rank. globalRank becomes globalRanking. Heatmap value becomes count. Streaks are local ceil intensity.
Upstream HTML changes will break fields before they break the envelope. Empty data with status: "error" is the usual miss. Cache that miss so you do not hammer CodeChef for ghosts.
End-to-end walk#
sequenceDiagram participant B as Browser participant API as codechef-stats participant CC as codechef.com B->>API: GET /tourist/stats API->>CC: GET /users/tourist Note over API: soup .rating-number .rating .user-country-flag Note over API: regex var all_rating and userDailySubmissionsStats API->>CC: GET /recent/user?user_handle=tourist&page=0..39 Note over API: XHR JSON, HTML content, table.dataTable accepted rows API->>CC: GET /api/contests/PRACTICE/problems/CODE Note over API: computed_tags else user_tags, TTLCache 6h API-->>B: envelope platform codechef
- Chrome UA GET
/users/{handle}. 404 →CodeChef user not found.favicon.ico→ 400. - BeautifulSoup
.user-details-containerfor avatar and name. .rating-numbercurrent rating. Parent textHighest Rating N..ratingstars."unrated"dropped from rank..user-country-flag/.user-country-name. Relative src made absolute againstcodechef.com..rating-ranks strong→ global then country rank.- Regex
var all_rating = ...;JSON list of contests. - Regex
var userDailySubmissionsStats = ...;dict or list. Dict becomes{date, value}rows. .rating-data-section.problems-solvedheadingTotal Problems Solved, else regex on the raw page.- Topics (only on
/statsand/topics): paginate/recent/userwithX-Requested-With: XMLHttpRequest, cap 40 pages, 10 concurrent. Each pagecontentis HTML. Rows with verdict titleacceptedcontribute a problem code. ThenGET /api/contests/PRACTICE/problems/{code}forcomputed_tagselseuser_tags. Tag cache 6h / 4096.
In-memory profile_cache (300s, 256) sits in front of step 1. Redis HTTP cache sits in front of the whole response. Request path.