contaix.web
Web content extraction and aggregation tools.
Functions for extracting structured content from websites and converting to markdown, with support for navigation discovery, caching, and content aggregation.
Main entry points:
site_to_markdown(url) # Full pipeline: discover nav, fetch pages, aggregate extract_site_nav(url) # Get ordered navigation structure from a doc site fetch_page(url) # Fetch a single page’s HTML with optional caching
Example:
>>> md = site_to_markdown(
... 'https://platform.claude.com/docs/en/home',
... cache_dir='/tmp/claude_docs_cache',
... output_file='~/Downloads/claude_docs.md',
... )
- contaix.web.extract_rsc_page_content(rsc_text: str) str | None[source]
Extract page content from RSC flight data.
Works on both: - RSC flight endpoint responses (
text/x-component) - Inline RSC payloads extracted from HTMLReturns markdown text if content was found, None otherwise.
Extract navigation structure from a documentation site.
- Returns a dict with keys:
base_url: The base URL of the sitetabs: List of tab dicts, each withlabelandgroupspages: Flat ordered list of{path, title}dicts (all tabs)
- Tries, in order:
Next.js RSC payload (embedded in SSR HTML)
HTML
<a>link extraction with path-based grouping
- Parameters:
url (str) – The documentation site URL (e.g. a homepage or any page on the site).
html (str, optional) – Pre-fetched HTML. If None, fetches from
url.
- contaix.web.fetch_llms_full(url: str, *, timeout: int = 30) str | None[source]
Fetch a publisher-provided single-doc bundle if available.
See
find_llms_full_url()for how the URL is discovered.Returns the markdown text, or
Noneif no bundle was found.
- contaix.web.fetch_nextjs_rsc(url: str, *, cache_dir: str = None, force: bool = False, timeout: int = 30) str[source]
Fetch page content via Next.js RSC flight endpoint.
Next.js App Router sites serve React Server Component payloads when the
RSC: 1header is present. These contain the full page content that would otherwise require JavaScript rendering.- Parameters:
url (str) – The page URL.
cache_dir (str, optional) – Directory to cache RSC responses.
force (bool) – Re-fetch even if cached.
timeout (int) – Request timeout.
- Returns:
The RSC flight data as text.
- Return type:
str
- contaix.web.fetch_page(url: str, *, cache_dir: str = None, force: bool = False, timeout: int = 30) str[source]
Fetch a page’s HTML content, with optional disk caching.
- Parameters:
url (str) – The URL to fetch.
cache_dir (str, optional) – Directory to cache HTML files. If None, no caching.
force (bool) – If True, re-fetch even if cached.
timeout (int) – Request timeout in seconds.
- Returns:
The page’s HTML content.
- Return type:
str
- contaix.web.fetch_pages(urls: Iterable[str], *, cache_dir: str = None, force: bool = False, verbose: bool = False) dict[str, str][source]
Fetch multiple pages with caching.
Returns a dict mapping URL -> HTML content. Pages that fail to fetch are skipped with a warning.
- contaix.web.find_llms_full_url(url: str, *, timeout: int = 30) str | None[source]
Probe for a publisher-provided single-doc bundle.
Many documentation generators (Mintlify, Docusaurus, Fern, etc.) expose the entire docs as a single markdown file at
/llms-full.txt(or/llms.txtfor an index). When present, this is far better than scraping page-by-page.Tries, in order:
Same path as the input URL with the doc-root replaced (e.g.
https://site.com/docs/foo->https://site.com/docs/llms-full.txt).Site root (e.g.
https://site.com/llms-full.txt).
- Parameters:
url (str) – Any URL on the documentation site.
timeout (int) – HEAD request timeout.
- Returns:
The first URL that returns HTTP 200 with non-trivial content, or
Noneif none of the candidates exist.- Return type:
str or None
- contaix.web.html_to_clean_markdown(html: str, *, body_width: int = 0, ignore_images: bool = True, include_links: bool = True, **html2text_options) str[source]
Convert HTML to clean markdown using html2text.
- Parameters:
html (str) – Raw HTML content.
body_width (int) – Line width for wrapping. 0 = no wrapping.
ignore_images (bool) – Whether to skip image tags.
include_links (bool) – Whether to include hyperlinks in output.
- contaix.web.is_nextjs_site(html: str) bool[source]
Detect if a page is served by Next.js (App Router).
Looks for
self.__next_f.pushin the HTML, which is the RSC streaming payload signature.
- contaix.web.list_site_pages(url: str) list[dict][source]
List all pages found in a documentation site’s navigation.
Returns a list of dicts with keys: path, title, url, group, tab.
>>> pages = list_site_pages('https://platform.claude.com/docs/en/home') >>> len(pages) 89
- contaix.web.parse_rsc_flight(rsc_text: str) dict[source]
Parse RSC flight data into a dict mapping keys to parsed JSON data.
Each RSC record starts with
<hex_key>:<payload>. Records come in three flavors:I[...]: module import descriptors (skipped here).T<hex_size>,<bytes>: a raw text chunk whose payload occupies exactlyhex_sizebytes (often markdown, code, or compiled MDX). The payload may contain newlines, so we read by byte count rather than splitting on\n.Anything else: a JSON value terminated by the next record header (
\n<hex>:) or end-of-text.
- Parameters:
rsc_text (str) – Raw RSC flight response text (
text/x-component).- Returns:
Mapping of hex keys to their parsed payloads.
- Return type:
dict
- contaix.web.rsc_tree_to_markdown(node, *, _registry: dict = None, _depth: int = 0) str[source]
Recursively extract markdown from a React Server Component tree node.
The RSC tree uses the format:
["$", "tagName", key, {"children": ..., "className": ...}]
or plain strings for text nodes. Component references like
"$L2a"are resolved via_registry(fromparse_rsc_flight()).
- contaix.web.site_to_markdown(url: str, *, cache_dir: str = None, output_file: str = None, tab_filter: str | Callable = None, page_fetcher: Callable = None, content_extractor: Callable = None, section_separator: str = '\n\n---\n\n', collapse_blank_lines: bool = True, use_llms_full: bool = True, verbose: bool = False) str[source]
Download a documentation site and produce a single aggregated markdown.
- Parameters:
url (str) – The documentation site’s root URL.
cache_dir (str, optional) – Directory for caching fetched HTML. Defaults to
~/.cache/contaix/web.output_file (str, optional) – Path to save the markdown file. If None, returns the string.
tab_filter (str or callable, optional) – If a string, only include pages from tabs whose label contains this string (case-insensitive). If a callable,
tab_filter(tab_label) -> bool.page_fetcher (callable, optional) – Custom
(url) -> htmlfunction. Defaults tofetch_page.content_extractor (callable, optional) – Custom
(html) -> markdownfunction. Defaults to auto-detection.section_separator (str) – Separator between page sections in the output.
use_llms_full (bool) – If True (default), first try to find a publisher-provided
/llms-full.txtbundle (Mintlify, Docusaurus, etc. ship these). When found, returns it directly instead of scraping page-by-page. Disable to force the scraping pipeline. Skipped automatically whenpage_fetcherortab_filteris set, since those imply the caller wants the scraping path.verbose (bool) – Print progress information.
- Returns:
If
output_fileis None, returns the markdown string. Otherwise, returns the output file path.- Return type:
str