Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the third-party nimquery package to run CSS selectors against Nim’s parsed HTML tree. Install it with nimble install nimquery, parse markup with htmlparser.parseHtml, then call querySelector for the first match or querySelectorAll for every match.

What you need

  • A working Nim installation and Nimble package manager.
  • The nimquery package. Install it from a shell with nimble install nimquery.
  • HTML to parse into an XmlNode tree. Nim’s htmlparser module creates that tree; it does not itself provide CSS-selector methods.

The selector API in this workflow belongs to nimquery, while parsing comes from Nim’s standard library.

Your first CSS-selector query

Create a file named selectors.nim:

from xmltree import `$`
from htmlparser import parseHtml
import nimquery

let html = """
<!DOCTYPE html>
<html>
  <head><title>Example</title></head>
  <body>
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
  </body>
</html>
"""

let xml = parseHtml(html)
let elements = xml.querySelectorAll("p:nth-child(odd)")
echo elements

Compile and run it:

nim c -r selectors.nim

The documented example selects the first and third paragraphs and prints an XML-node sequence equivalent to @[<p>1</p>, <p>3</p>]. The example demonstrates the library’s documented usage; treat the exact output formatting as dependent on the installed Nim and package versions.

Choosing between querySelector and querySelectorAll

Get every match

Call querySelectorAll(root, selector, options) when all matching elements matter:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
let cards = xml.querySelectorAll(".card")
for card in cards:
  echo $card

The result is a sequence of matching XmlNode values. An empty sequence means that no node matched; it is not an error.

Get the first match

Call querySelector(root, selector, options) when you need one element:

let title = xml.querySelector("title")
if title.isNil:
  echo "No title found"
else:
  echo $title

querySelector returns the first matching node or nil. Always check for nil before dereferencing or serializing the result.

Reuse a parsed query

For repeated execution, parse the selector once:

import nimquery

let query = parseHtmlQuery("article p")
let first = query.exec(xml, single = true)
let all = query.exec(xml, single = false)

parseHtmlQuery parses a selector for later use. Calling exec with single = true limits the result to at most one element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Selectors and supported CSS behavior

The README describes support for CSS3 selectors, with explicit exceptions. Do not assume browser-level support for selectors that depend on interaction state, browser navigation, language matching, or pseudo-elements.

Selectors explicitly not supported

  • :root
  • :link, :visited, :active, :hover, :focus, and :target
  • :lang(...), :enabled, :disabled, and :checked
  • ::first-line, ::first-letter, ::before, and ::after

These states and pseudo-elements describe browser rendering or interaction. A static parsed HTML tree has no browser event state or generated-content layer for nimquery to inspect.

Use :not carefully

The default QueryOption set includes optSimpleNot. With that option enabled, arguments passed to :not(...) must be simple selectors. If you need a more complex argument, remove optSimpleNot, following the package’s documented pattern:

let options = {optUniqueIds, optUnicodeIdentifiers}
let nodes = xml.querySelectorAll("p:not(.muted)", options)

Removing optSimpleNot relaxes the simple-argument restriction, but combinators still are not allowed inside the :not argument according to the documented behavior. Check the installed package README before relying on a complex selector.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Query options

QueryOption includes:

  • optUniqueIds: an assumption that IDs in the queried document are unique. Use it only when that assumption fits your input.
  • optSimpleNot: restricts :not(...) to simple selectors.
  • optUnicodeIdentifiers: enables the documented handling of Unicode identifiers.

The documented default set is { optUniqueIds, optUnicodeIdentifiers, optSimpleNot }. Option semantics are library-specific, so review the version of the README installed with your project before making performance or matching guarantees.

Parsing HTML before querying

Parse a string

parseHtml is convenient when markup is already in memory:

from htmlparser import parseHtml

let root = parseHtml("<main><h1>Nim</h1></main>")
let heading = root.querySelector("h1")

Parse from a stream

The project README also demonstrates parsing from a newStringStream. This is useful when another part of your program supplies a stream rather than a complete string. Keep parsing and selection as separate stages so parse failures can be handled before selector execution.

Understand the tree you are querying

Nim’s HTML parser creates an XML-tree representation. Selectors match nodes in that representation, not a live browser DOM. JavaScript-generated elements, layout, computed styles, hover states, and pseudo-element content are not created by parseHtml.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Error handling and troubleshooting

“Undeclared identifier: querySelectorAll”

Cause: nimquery is not imported, or the package is not installed for the compiler environment you are using.

Fix: Run nimble install nimquery and add import nimquery to the source file. Compile again with the same Nim installation that owns the Nimble package path.

Selector parsing raises ParseError

Both selector methods document ParseError when the selector string cannot be parsed. Check spelling, brackets, quotes, pseudo-class syntax, and combinators. Reduce the selector to a simple element or class selector, then add parts back one at a time.

try:
  let matches = xml.querySelectorAll("article[")
  echo matches
except ParseError as error:
  echo "Invalid selector: ", error.msg

No elements are returned

  • Confirm that the HTML was parsed successfully and that the selector matches the parsed structure.
  • Remember that selectors operate on the static input; they cannot see content injected by JavaScript.
  • Check whether a selector is on nimquery’s unsupported list.
  • Inspect element names, classes, and IDs in the parsed tree. HTML casing and malformed markup can produce a tree different from what you expected.

querySelector returns nil

This is normal when there is no match. Test the result before using $node or accessing child nodes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

:not(…) does not parse

With the default optSimpleNot, use a simple argument such as :not(.hidden). If the required argument is more complex, pass options without optSimpleNot, while observing the remaining restriction on combinators.

Building a reusable helper

A small helper can make missing matches explicit and keep selector errors at the boundary of your program:

import std/[strformat]
from htmlparser import parseHtml
import nimquery

proc firstOrReport(root: XmlNode, selector: string): XmlNode =
  try:
    result = root.querySelector(selector)
  except ParseError as error:
    raise newException(ValueError, fmt"Invalid CSS selector '{selector}': {error.msg}")

let root = parseHtml("<section><p class='lead'>Hello</p></section>")
let lead = firstOrReport(root, ".lead")
if lead.isNil:
  echo "The selector matched nothing"
else:
  echo $lead

This separates three outcomes: valid selector with a match, valid selector with no match, and invalid selector syntax.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and reliability considerations

  • Parse the HTML once and reuse the resulting tree for multiple selectors.
  • Use querySelector when you only need one match; use querySelectorAll when you need the complete set.
  • For repeated selectors, use parseHtmlQuery and execute the parsed query rather than reparsing its text each time.
  • Use optUniqueIds only when your documents satisfy its unique-ID assumption.
  • Keep selectors specific enough to express intent, but verify them against representative malformed and well-formed documents.

The available documentation does not establish a current release matrix, compiler compatibility table, benchmark, or maintenance guarantee. Confirm those details for the nimquery version and Nim toolchain you deploy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If your real goal is to capture a rendered page rather than query a local HTML tree, ScreenshotNeo provides a one-request screenshot API. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

Use the API with cURL (see the ScreenshotNeo documentation):

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same request in Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every plan includes the full feature set, including full-page and element capture, device and retina settings, PDF controls, custom CSS and JavaScript, waits, request blocking, headers and cookies, geolocation, caching, signed links, asynchronous jobs, bulk capture, usage data, and an OpenAPI specification. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

FAQ

Is CSS-selector querying built into Nim?

No. The documented selector methods in this workflow come from the third-party nimquery package; Nim’s standard library supplies HTML parsing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What does querySelector return when there is no match?

nil. Check it before using the returned node.

Can nimquery select CSS pseudo-elements?

No. The documented unsupported list includes ::before, ::after, ::first-line, and ::first-letter, along with several interaction-state pseudo-classes.

How do I select all matching nodes?

Parse the HTML and call querySelectorAll with the selector string. It returns a sequence of matching XmlNode values.

Frequently Asked Questions

Does nimquery execute JavaScript before selecting?

No. It queries the static tree produced by Nim’s HTML parser; JavaScript-generated DOM is not available.

Can I reuse one selector efficiently?

Yes. Use parseHtmlQuery to parse it once, then call exec with single=true or single=false as needed.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Bottom Line

Install nimquery, parse HTML with parseHtml, and choose querySelector or querySelectorAll according to whether you need one match or all matches. Validate selectors against nimquery’s documented CSS3 subset and handle ParseError and nil explicitly.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.