NkkinParser 2.1.1
dotnet add package NkkinParser --version 2.1.1
NuGet\Install-Package NkkinParser -Version 2.1.1
<PackageReference Include="NkkinParser" Version="2.1.1" />
<PackageVersion Include="NkkinParser" Version="2.1.1" />
<PackageReference Include="NkkinParser" />
paket add NkkinParser --version 2.1.1
#r "nuget: NkkinParser, 2.1.1"
#:package NkkinParser@2.1.1
#addin nuget:?package=NkkinParser&version=2.1.1
#tool nuget:?package=NkkinParser&version=2.1.1
NkkinParser
A fast, low-allocation HTML5 parser and CSS selector engine for .NET 10. No dependencies.
NkkinParser builds the same DOM a browser would from messy real-world HTML, then lets you query it with CSS selectors. On the project's benchmarks it parses about 2.7× faster than AngleSharp and 3.3× faster than HtmlAgilityPack with roughly half of AngleSharp's allocations, and it matches AngleSharp's results on 28 real pages (identical body text, identical results for 840 selector checks).
Install
dotnet add package NkkinParser
Targets net10.0.
Quick start
using NkkinParser;
var document = HtmlParser.Parse(html);
foreach (var link in document.QuerySelectorAll("a[href]"))
Console.WriteLine($"{link.TextContent} -> {link.ResolveUrl()}");
Console.WriteLine(document.Title);
Console.WriteLine(document.QuerySelector("article")?.ToPlainText());
// Selectors Level 4 and a DOM you can edit.
foreach (var card in document.QuerySelectorAll("div.card:has(> img):not(:is(.ad, .promo))"))
{
card.AddClass("seen");
card.QuerySelector("script")?.Remove();
}
More entry points:
using var fromBytes = new HtmlParser(bytes); // encoding detected from BOM / <meta charset>
var arabic = HtmlParser.Parse(bytes, EncodingDetector.GetEncoding("windows-1256")); // HTTP charset wins over <meta>
var fromStream = HtmlParser.Parse(File.OpenRead("page.html"));
var fromUrl = await HtmlLoader.LoadAsync("https://example.com/"); // HTTP/HTTPS, compressed, size-capped, header charset, BaseUri set
var index = new NkkinParser.Indexing.DocumentIndex(document); // build once, reuse for many queries
var cards = document.QuerySelectorAll("div.card > a[href], #search", index);
string html2 = element.OuterHtml; // also InnerHtml, TextContent
Streaming without a DOM:
var reader = new NkkinPullParser(html);
while (reader.Read())
if (reader.CurrentType == NodeType.Element && reader.TryGetAttribute("href", out var href)) Console.WriteLine(href);
await foreach (var node in HtmlParser.ParseAsync(stream, cancellationToken)) { /* lazily yielded nodes */ }
Features
- Browser-like tree construction: implied
html/head/body, the WHATWG adoption agency algorithm, tables with foster-parenting and impliedtbody/tr,<template>, auto-closingp/li/dt/dd/headings/option/ruby, scope-aware end tags, and correct raw-text handling forscript,style,title,textarea,iframe,xmp. - Inline SVG and MathML:
Element.Namespace, camelCase names kept (viewBox,foreignObject),/>honoured. - All 2,231 HTML named entities, legacy names without a semicolon, and the attribute ambiguity rule.
- Encoding detection from the BOM, the HTTP charset and
<meta charset>, in browser order;latin1/iso-8859-1/asciidecode as windows-1252, as in browsers. - Selectors: type,
*,#id,.class(with CSS escapes such as.md\:flex), all attribute operators (withi), combinators (,>,+,~), lists,:is(),:where(),:has(),:not(),:scope,:first-child,:last-child,:only-child,:nth-child(An+B [of S]),:nth-last-child(), the-of-typefamily,:empty,:root,:checked,:disabled,:enabled,:required,:optional,:read-only,:read-write,:placeholder-shown,:link/:any-link, and the jQuery-style:contains("text"). Unsupported pseudo-classes match nothing. - DOM API:
Id,ClassList,GetAttribute/SetAttribute/RemoveAttribute,AddClass/RemoveClass,Children,Closest,Matches,Descendants(),AppendChild/InsertBefore/Remove/ReplaceWith/CloneNode,Document.Title/Head/Body/CreateElement,ResolveUrl()(honours<base href>), andToPlainText(). - Robust on untrusted input: iterative tree walks (100,000-level nesting is fine), bounded selector cache, response-size cap in
HtmlLoader, seeded fuzz tests. - Opt-in node pooling (
new HtmlParser(html, enableNodePooling: true)) for parse loops; do not read the tree after disposing the parser in that mode.
How the DOM behaves
<p>x</p>becomeshtml > head + body > p, soQuerySelector("body")always works.- Text is whitespace-collapsed and trimmed at block boundaries, keeping one space between inline elements (
Hello <b>world</b>readsHello world);pre,listing,textarea,scriptandstyleare verbatim. <template>content is inert, as in a browser: document-level queries,DocumentIndexand an ancestor'sTextContentskip it. Query the template element itself (Element.IsTemplate) to reach it.- Comment and doctype nodes hold only their data or name, not the delimiters.
- A tag still open at the end of the input is dropped.
Performance
Typical of a few short BenchmarkDotNet runs (i3-8100, .NET 10, AngleSharp 1.8.2, HtmlAgilityPack 1.13.0), on synthetic documents. Compare the ratios; absolute times vary by machine (about ±10% between runs).
| Benchmark | AngleSharp | HtmlAgilityPack | NkkinParser |
|---|---|---|---|
| Parse (three documents, ~10 KB to 1 MB) | 110 ms / 40 MB | 134 ms / 79 MB | 41 ms / 21 MB |
| Parse + serialize | 240 ms / 60 MB | 200 ms / 102 MB | 50 ms / 25–33 MB |
| CSS selector query, 6,002 matches | 0.70 ms / 128 KB | 312 ms / 4,977 KB (XPath) | 0.36 ms / 51 KB |
Allocation figures were confirmed with GC.GetAllocatedBytesForCurrentThread because BenchmarkDotNet's allocation column is unreliable on this suite. Methodology, the full tables and reproduction steps are in the repository README.
Verification
323 unit tests, seeded fuzzing, about 87% of the html5lib tree-construction cases (86.8% of 957 at this release), and a real-page comparison against AngleSharp. See How it is verified.
Limitations
Not a browser engine (no scripting, CSS cascade or layout); <frameset> and <plaintext> are not modelled; a few deep table/formatting corner cases differ from the standard; no user-action pseudo-classes (:hover, :focus) or pseudo-elements; no fragment parsing (InnerHtml is read-only).
Links
- Full README
- Changelog, including the breaking changes in 2.0.0
MIT licensed.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
2.1.1: fixes selector validation and index isolation, form-state matching, meta charset detection, SVG/MathML raw-text handling, DOM root tracking when moving nodes, and ToPlainText() on preformatted or skipped elements. See https://github.com/Nkkinsoft/NkkinParser/blob/main/CHANGELOG.md#211.