cull / htmlq alternative

Migrating from htmlq to cull

htmlq ("like jq, but for HTML") is a fine tool for pulling raw HTML, text, or attribute values out of a page with CSS selectors. But its last released version, v0.4.0, dates to January 2022, it has dozens of long-open issues, and it stops at raw output — the moment you need structured data (JSON objects, CSV rows, Markdown) you're back to piping through sed, paste, and jq -R.

cull is an actively maintained alternative written in Rust. Every common htmlq invocation has a direct, usually shorter, cull equivalent — and the output side goes much further.

Flag-for-flag migration table

htmlqcull
htmlq 'a'cull 'a'
htmlq 'a' --textcull 'a' -t
htmlq 'a' --attribute hrefcull 'a' -a href
htmlq 'a' -a href --base URLcull 'a' -a href -b URL — automatic when the input is a URL
htmlq --remove-nodes nav 'a'cull 'a' -r nav
htmlq 'a' --filename page.htmlcull 'a' page.html
htmlq --pretty '#main'cull -p '#main' — indented, syntax-colored on a TTY, and rendering-faithful: htmlq's --pretty deletes significant spaces between inline elements (#58); cull never changes what the page renders. Whole-document output also keeps the <!DOCTYPE> htmlq drops (#56)
htmlq 'div:has(p)'panics (htmlq#65)cull 'div:has(p)':has(), :is(), :where() all work

What htmlq has no equivalent for

# Interactive selector mode — live preview TUI, Tab cycles output shape
$ cull -I https://example.com
# Shaped JSON — a jq-style template turns each match into a clean object (NDJSON)
$ cull '.athing.submission' -j '{title: .titleline > a, url: .titleline > a @href}' https://news.ycombinator.com
{"title":"…","url":"https://…"}
# Any <table> to CSV (or NDJSON keyed by the header row) — colspan/rowspan expanded
$ cull --table page.html
$ cull --table --json-rows page.html
# Page (minus chrome) to Markdown — e.g. to feed an article to an LLM
$ cull article --md -r 'nav, footer, script, style' https://example.com/post
# Fetch the URL yourself — no curl needed, relative links resolve automatically
$ cull a -a href https://example.com
# Multiple files and globs, grep-style -c / -l
$ cull 'meta[name=generator]' -l snapshots/*.html

And one correctness gap: htmlq decodes every page as UTF-8, so Shift_JIS, KOI8-R, and windows-125x pages come out as mojibake. cull sniffs the BOM, the Content-Type header, and <meta charset> the way a browser does.

Fair notes: if all you need is --text or --attribute on UTF-8 pages you already fetch with curl, htmlq still does that job. Differences to know when switching: cull collapses whitespace to one line per match in -t mode, and exits 1 when nothing matched (grep-style), which makes if cull … ; then conditionals work.

Install cull

# Homebrew
$ brew install rashida-thorne/cull/cull
# or: curl | sh, scoop, nix, cargo binstall, cargo install — see the site

All options, prebuilt static binaries, and a demo GIF on the cull homepage. Benchmarked 1.2–1.6× faster than htmlq on typical pages, with a reproducible script.

Something in your htmlq workflow missing from the table above? Open an issue — migration gaps are treated as bugs.