Skip to main content

Scrape Command

Scrapes documentation URLs and converts each page into an MDX file ready for Mintlify.

Usage

writechoice scrape # reads urls.json by default
writechoice scrape --urls-file custom.json
writechoice scrape https://docs.example.com/page

Options

OptionDescriptionDefault
[urls...]One or more URLs to scrape
--urls-file <file>JSON file with an array of URLsurls.json
-o, --output <dir>Output directory for MDX filesoutput
--playwrightUse Playwright for JavaScript-rendered pagesfalse
-c, --concurrency <n>Number of parallel requests3
--dry-runPreview output without writing filesfalse
--quietSuppress terminal outputfalse

Examples

# Scrape a single page
writechoice scrape https://docs.example.com/getting-started

# Scrape multiple URLs
writechoice scrape https://docs.example.com/overview https://docs.example.com/api

# Scrape from a JSON file
writechoice scrape --urls-file urls.json --output my-docs

# Use Playwright for a JavaScript-rendered site
writechoice scrape --playwright --urls-file urls.json

URL File Format

urls.json should be a JSON array of strings:

[
"https://docs.example.com/getting-started",
"https://docs.example.com/installation",
"https://docs.example.com/api-reference/overview"
]

URL → File Mapping

URLOutput file
https://example.com/docs/overviewoutput/docs/overview.mdx
https://example.com/api/v2/introoutput/api/v2/intro.mdx
https://example.com/output/index.mdx

Frontmatter Output

Each scraped page produces frontmatter with:

  • title — extracted from title_selector or <title> tag; site name suffixes are stripped
  • permalink — the original URL of the page
  • og:title, og:description, og:image — when present in meta tags

Configuration

Add a scrape section to config.json for advanced control. See the Config Reference for the full list of options with descriptions.

Content Selectors

KeyDescriptionDefault
content_selectorCSS selector for the main content areabody
title_selectorCSS selector for the page titleh1
elements_to_removeCSS selectors of elements to strip[]
html_preserve_elementsHTML tags to keep as raw HTML["table", "iframe"]
html_preserve_customAdditional CSS selectors to preserve as raw HTML[]

Image Strategies

StrategyBehavior
keep_remoteKeep original remote URLs unchanged (default)
download_by_urlDownload images; save using the image URL's path
download_by_pageDownload images; save under the page's slug directory

Component Mappings

scrape.components is an array of component definitions. Each entry tells the scraper how to find an HTML pattern and convert it into a Mintlify MDX component. The name you provide becomes the JSX tag — so "name": "Note" produces <Note>, "name": "Accordion" produces <Accordion>, and so on.

Native <details>/<summary> elements are always converted to <Accordion> automatically, regardless of config.

Component Definition

KeyTypeDescription
namestringMDX component name — becomes the JSX tag
selectorstringCSS selector matching each component element
propsobjectMap of prop name → extraction rule (see below)
contentstringCSS selector for inner content (defaults to full innerHTML)
groupobjectOptional grouping config (see below)

Extracting Props

FormExampleBehavior
String"title": ".callout-title"Finds the child element, uses its text; element removed from content
Object with selector"title": { "selector": ".callout-title" }Same as string shorthand
Object with selector + attr"icon": { "selector": "img", "attr": "src" }Reads an attribute from the child element
Object with attr only"href": { "attr": "href" }Reads an attribute from the matched element itself
Add "image": true"img": { "attr": "data-src", "image": true }Image URL — downloaded and resolved per your images strategy
Add "child": true"title": { "selector": ".title", "child": true }Renders as **bold text** inside the component body instead of a JSX prop

Grouping

FormBehavior
"group": { "selector": ".container", "wrapper": "AccordionGroup" }Finds each container, collects items inside, wraps in wrapper
"group": { "wrapper": "AccordionGroup" }Auto-groups consecutive sibling matches into one wrapper
No group keyEach match is converted independently

Examples

Callouts (<Note>, <Warning>, <Tip>, …)

Mintlify callout titles are children, not props — use "child": true so the title renders as **bold text** inside the component:

[
{
"name": "Note",
"selector": ".admonition.note",
"props": { "title": { "selector": ".admonition-title", "child": true } },
"content": ".admonition-body"
},
{
"name": "Warning",
"selector": ".admonition.warning",
"props": { "title": { "selector": ".admonition-title", "child": true } },
"content": ".admonition-body"
}
]

Output:

<Note>
**My callout title**

Content here...
</Note>

For components that accept title as a JSX prop (e.g. <Accordion>), omit "child": true.

Accordions (explicit group container)

[
{
"name": "Accordion",
"selector": ".faq-item",
"group": { "selector": ".faq-section", "wrapper": "AccordionGroup" },
"props": { "title": ".faq-question" },
"content": ".faq-answer"
}
]

Accordions (auto-group consecutive siblings)

[
{
"name": "Accordion",
"selector": ".accordion-item",
"group": { "wrapper": "AccordionGroup" },
"props": { "title": ".accordion-header" },
"content": ".accordion-body"
}
]

Cards with image and link

[
{
"name": "Card",
"selector": "a.card",
"group": { "wrapper": "CardGroup" },
"props": {
"title": "h3.card-title",
"icon": { "attr": "data-icon" },
"href": { "attr": "href" },
"img": { "selector": "img.card-thumb", "attr": "src", "image": true }
},
"content": ".card-description"
}
]

Tabs

[
{
"name": "Tab",
"selector": ".tab-panel",
"group": { "selector": ".tabs", "wrapper": "Tabs" },
"props": { "title": { "attr": "data-tab-title" } }
}
]

Playwright Config

KeyDescriptionDefault
headlessRun browser in headless modetrue
wait_for_selectorWait for this CSS selector before capturingnull
wait_timeSeconds to wait for JS to settle3
page_load_timeoutMax seconds to wait for page load30
storage_statePath to a session file from writechoice sessionnull

Script Hooks

For cases where config options aren't enough, inject custom JavaScript at two points in the pipeline — before HTML-to-Markdown conversion (pre) or after (post). See the Script Hooks guide for details and examples.

Authenticated Scraping

For sites behind a login, use writechoice session to capture your authenticated session, then reference it in playwright_config.storage_state.