Add CCO scraper - #17
Open
NisargOza wants to merge 1 commit into
Open
Conversation
|
|
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.
What this does
Adds a Cancer Care Ontario (CCO) scraper to the datasets scraping pipeline. Same shape as the NICE scraper — discovery and extraction are separate, and everything outputs a normalized
ScrapedDocument.Unlike NICE, CCO sits behind an Azure WAF JS challenge that rejects plain HTTP requests regardless of headers, so this scraper drives a real headless browser (Playwright) instead of
httpx. Every function that touches the network takes a plainfetch(url) -> htmlcallable rather than a browser handle directly, so discovery and parsing stay unit-testable without a browser.The scraper extracts each guideline's short summary sections (objective, patient population, intended users, etc.) and bibliographic metadata (ID, version, type of content, document status, authors). It does not fetch the linked PDF, matching this repo's existing HTML-over-PDF stance for NICE — the PDF is recorded as a markdown link and as
metadata["pdf_url"]for a later PDF-aware pass. It also handles several distinct page templates spanning roughly two decades of CCO's CMS history, including guidelines with no narrative sections at all, and guidelines archived 5+ years where CCO itself hides the abstract and PDF behind an HTML comment.scrape_listing_documentsinbase.pyis generalized to accept any client type, not justhttpx.Client(NICE is unaffected — its type is inferred the same as before).How discovery works
Discovery starts from CCO's
types-of-cancerindex page, which is itself the full master listing of every guideline — the per-cancer-type category buttons on it are just filtered subsets of the same list, so they aren't crawled separately. The listing is paged through a Drupal "Load more" pager rather than arel=nextlink, so pagination follows that pager's markup directly.Discovery retries itself up to 3 times if it comes back with a suspiciously small number of guidelines (fewer than 50, when CCO reports several hundred) — a thin first result turned out to be a load-timing issue rather than a real "no guidelines" state. If it's still thin after retries, it raises loudly rather than silently reporting a false "0 documents scraped" success.
How extraction works
Each guideline page is fetched through Playwright (resolving CCO's WAF challenge the same way a real browser would) and parsed with lxml. Bibliographic fields (
Version,ID,Type of Content,Document Status,Authors) and the PDF link are matched against the whole page — they're precise, unambiguous matches, and at least one CCO template puts this metadata block outside the page's main-content region entirely. Narrative sections (Guideline Objective,Patient Population,Intended Guideline Users,Research Question(s), etc.) are matched preferentially within the page'srole="main"/#contentregion, falling back to the whole page only if that comes up empty — this stops global nav/footer boilerplate from being absorbed into whichever section happens to be last on the page.The extraction preserves:
metadataand as inline content when no narrative sections existmetadata["pdf_url"]<h1>when CCO's own<title>tag comes back empty (observed on some archived pages)Guidelines that fail to parse (an unrecognized template, a fetch error, a persistent WAF-challenge failure) don't abort the run — they're yielded as a placeholder document with
metadata["scrape_error"]set, so a run across ~400 guidelines spanning many CMS eras can complete even if a handful of pages don't match anything recognized.CLI
uv run playwright install chromium # one-time browser binary download
uv run amfv-scrape --source cco --documents 1
uv run amfv-scrape --source cco --url https://www.cancercareontario.ca/en/guidelines-advice/types-of-cancer/64736
uv run amfv-scrape --source cco --documents 5 -f markdown -o ./cco-out/
uv run amfv-scrape --source cco --documents all -o cco.jsonl
--source allnow includes CCO alongside NICE.Licensing
CCO / Ontario Health guideline content appears on Meditron's list of redistributable sources for this kind of pipeline, but we haven't independently confirmed a redistribution license directly from CCO/Ontario Health. Treat bulk reuse as needing the same confirmation step as any other source until that's verified.
QA
metadata["scrape_error"].Test plan
uv run pytest datasets/test/test_scraping_cco.py— URL validation, field/section parsing across all three known templates, Load-more pagination, WAF-challenge and transient-error retries, discovery sanity-check retry, per-item failure resilienceuv run pytest— no regressions (39 passed)uv run ruff check ./uv run ruff format --check .uv run amfv-scrape --source cco --documents 5 -f markdown -o /tmp/cco-out/--documents all) to confirm discovered/scraped counts and spot-check any placeholder records