# Extract Data from HTML with a CSS Selector

Paste HTML, type a CSS selector, and pull out the text, inner/outer HTML, or an attribute from every match. Runs in your browser, nothing uploaded, free.

## Run it

- **CLI:** `gizza tool html-extract '<ul>
  <li><a href="/one">First</a></li>
  <li><a href="/two">Second</a></li>
</ul>' 'selector=a'`
- **Web:** https://gizza.ai/tools/html-extract/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/html-extract/tool.json

## Inputs

- `html` — HTML _(field)_
- `selector` — CSS selector _(field)_
- `extract` — Extract _(field)_
- `attr` — Attribute name (for Attribute mode) _(field)_
- `limit` — Max matches _(field)_
- `trim` — Normalize whitespace _(field)_

## Output

- Matches (JSON) (text)

## Query parameters

Open the tool pre-filled and auto-run via URL:

- `html` — HTML
- `selector` — CSS selector
- `extract` — Extract
- `attr` — Attribute name (for Attribute mode)
- `limit` — Max matches
- `trim` — Normalize whitespace

Example: `https://gizza.ai/tools/html-extract/?html=%3Cul%3E%0A%20%20%3Cli%3E%3Ca%20href%3D%22%2Fone%22%3EFirst%3C%2Fa%3E%3C%2Fli%3E%0A%20%20%3Cli%3E%3Ca%20href%3D%22%2Ftwo%22%3ESecond%3C%2Fa%3E%3C%2Fli%3E%0A%3C%2Ful%3E&selector=a&extract=text&attr=href&limit=100&trim=true`

---

## About this tool

This is "jq for markup": paste raw HTML, write a **CSS selector**, and pull the
same piece out of every element that matches. Pick what to extract — the visible
**text**, the element's **inner HTML** (its children), its **outer HTML** (the
element itself, tags and all), or the value of a named **attribute** such as
`href` or `src`. The result is JSON with a `count` and a `matches` array, so it
drops straight into a script or a spreadsheet.

Everything runs locally in your browser via WebAssembly — the HTML you paste is
never uploaded.

### Worked example

Paste this HTML:

```html
<ul>
  <li><a href="/one">First</a></li>
  <li><a href="/two">Second</a></li>
</ul>
```

With selector `a` and **Extract = Attribute**, attribute name `href`, you get:

```json
{
  "count": 2,
  "matches": [
    "/one",
    "/two"
  ]
}
```

Switch **Extract** to *Text* and the same selector returns `["First", "Second"]`
instead. Use a tighter selector (for example `li:first-child a`) to narrow the
matches, or raise **Max matches** if you have more than 100 results.

## FAQ

<!-- FAQ MUST be <details>/<summary> accordions: tools/generator/assets/runtime/tool.css styles them and
     scripts/check-tool-hygiene.py fails the build on a plain-markdown FAQ. Keep
     the blank line inside each <details> so the answer's markdown (inline
     `code`, **bold**, lists) renders and gets wrapped in <p>. One <details> per
     question; write real Q&A, not these TODOs. -->

<details>
<summary>What selectors are supported?</summary>

Standard CSS selectors: tag names (`p`), classes (`.link`), IDs (`#main`),
attribute selectors (`a[href]`), combinators (`div > p`, `ul li`), and
pseudo-classes like `:first-child` and `:nth-of-type(2)`. XPath is **not**
supported — this tool is CSS-only.

</details>

<details>
<summary>What's the difference between inner HTML and outer HTML?</summary>

For `<div><b>hi</b></div>` selected by `div`, **inner HTML** returns
`<b>hi</b>` (just the children) while **outer HTML** returns
`<div><b>hi</b></div>` (the element itself, its own tags included). **Text**
returns just `hi`.

</details>

<details>
<summary>How do I pull an attribute like a link's URL or an image's source?</summary>

Set **Extract** to *Attribute* and type the attribute name — `href` for links,
`src` for images, `class`, `id`, `data-*`, and so on. Elements that match the
selector but don't have that attribute are skipped, so the `count` reflects only
the elements that actually had a value.

</details>

<details>
<summary>Why is the whitespace collapsed in my results?</summary>

**Normalize whitespace** is on by default: it collapses runs of spaces and
newlines in text and attributes to single spaces and trims the ends of HTML.
Turn it off to get the exact original text, indentation and all.

</details>

<details>
<summary>Is my HTML uploaded anywhere?</summary>

No. The parser is compiled to WebAssembly and runs entirely in your browser, so
the HTML you paste never leaves your machine.

</details>

## Related tools

- [Extract Substring](https://gizza.ai/tools/extract-substring/): Pull out a portion of text by start/end character index, or everything between two delimiters — in your browser. Nothing is uploaded.
- [HTML to Text](https://gizza.ai/tools/html-to-text/): Strip HTML markup to clean, readable plain text in your browser — keeps paragraph and list structure, removes all tags. Free, private, no upload, no sign-up.
- [Code Chunker](https://gizza.ai/tools/code-chunker/): Split Python, Rust, JavaScript, TypeScript, Go, Java, C/C++, C#, PHP, or Swift into line-ranged chunks that keep functions and classes intact.
- [Code Formatter](https://gizza.ai/tools/code-formatter/): Beautify and re-indent minified or messy HTML, CSS, JavaScript, or JSON. Auto-detect the language, choose spaces or tabs, and format locally in your browser.
- [CSS Autoprefixer](https://gizza.ai/tools/css-autoprefixer/): Free online CSS autoprefixer — paste CSS and add the -webkit-, -moz-, -ms-, -o- vendor prefixes browsers still need. Runs entirely in your browser.
