cull / htmlq alternative
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.
| htmlq | cull |
|---|---|
htmlq 'a' | cull 'a' |
htmlq 'a' --text | cull 'a' -t |
htmlq 'a' --attribute href | cull 'a' -a href |
htmlq 'a' -a href --base URL | cull '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.html | cull '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 |
# 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.
# 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.