Free tools Windows power users keep installed
One-click scans. No signup required.
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
nimquerypackage. Install it from a shell withnimble install nimquery. - HTML to parse into an
XmlNodetree. Nim’shtmlparsermodule 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:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorslet 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.
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.
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.
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.
Rank #4
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.
: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.Performance and reliability considerations
- Parse the HTML once and reuse the resulting tree for multiple selectors.
- Use
querySelectorwhen you only need one match; usequerySelectorAllwhen you need the complete set. - For repeated selectors, use
parseHtmlQueryand execute the parsed query rather than reparsing its text each time. - Use
optUniqueIdsonly 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.
Best Value
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.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →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.
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.
Quick Recap
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.

