SoftCircuits.HtmlMonkey 2.1.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package SoftCircuits.HtmlMonkey --version 2.1.0                
NuGet\Install-Package SoftCircuits.HtmlMonkey -Version 2.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SoftCircuits.HtmlMonkey" Version="2.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SoftCircuits.HtmlMonkey --version 2.1.0                
#r "nuget: SoftCircuits.HtmlMonkey, 2.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SoftCircuits.HtmlMonkey as a Cake Addin
#addin nuget:?package=SoftCircuits.HtmlMonkey&version=2.1.0

// Install SoftCircuits.HtmlMonkey as a Cake Tool
#tool nuget:?package=SoftCircuits.HtmlMonkey&version=2.1.0                

HtmlMonkey

NuGet version (SoftCircuits.HtmlMonkey)

Install-Package SoftCircuits.HtmlMonkey

Overview

HtmlMonkey is a lightweight HTML/XML parser written in C#. It allows you to parse HTML or XML into a hierarchy of document node objects, which can then be traversed, or queried using jQuery-like selectors. The node objects can be modified or even built from scratch using code. Finally, you can use the classes to generate HTML or XML strings from the data.

The code also include a WinForms application to display the parsed data nodes. This was mostly done for testing the parser, but offers some functionality that may be useful for inspecting the original markup.

Getting Started

You can use either of the static methods HtmlDocument.FromHtml() or HtmlDocument.FromFile() to parse HTML and create an HtmlDocument object. (Note: If you're using WinForms, watch out for conflict with System.Windows.Forms.HtmlDocument.)

Parse an HTML Document
string html = "...";   // HTML markup
HtmlDocument document = HtmlDocument.FromHtml(html);

This code parses the HTML document into a hierarchy of nodes, which are then stored in the HtmlDocument object.

The node types include HtmlElementNode, which represents an HTML tag with attributes and any number of child nodes. HtmlTextNode nodes contain only text. And HtmlCDataNode nodes contain text from the document that was parsed but is otherwise ignored. Examples of content placed in HtmlCDataNode nodes include CDATA content, comments and the content of <script> tags.

The code also supports the specialized HtmlHeaderNode and XmlHeaderNode nodes.

HtmlMonkey provides a number of ways to navigate parsed nodes. The HtmlDocument.RootNodes property contains the root nodes in the document. Each HtmlElementNode node includes a Children property, which can be used to access all the other nodes in the document. In addition, all nodes have NextNode, PrevNode, and ParentNode properties, which you can use to navigate the nodes in every direction.

The HtmlDocument class also includes a Find() method, which accepts a predicate argument. This method will recursively find all the nodes in the document for which the predicate returns true, and return those nodes in a flat list.

// Returns all nodes that are the first node of its parent
IEnumerable<HtmlNode> nodes = document.Find(n => n.PrevNode == null);

You can also use the FindOfType() method. This method traverses the entire document tree to find all the nodes of the specified type.

// Returns all text nodes
IEnumerable<HtmlTextNode> nodes = document.FindOfType<HtmlTextNode>();

The FindOfType() method is also overloaded to accept an optional predicate argument.

// Returns all HtmlElementNodes that have children
IEnumerable<HtmlElementNode> nodes = document.FindOfType<HtmlElementNode>(n => n.Children.Any());

Using Selectors

The HtmlDocument.Find() method also has an overload that supports using jQuery-like selectors to find nodes. Selectors provide a powerful and flexible way to locate nodes.

Specifying Tag Names

You can specify a tag name to return all the nodes with that tag.

// Get all <p> tags in the document
// Search is not case-sensitive
IEnumerable<HtmlElementNode> nodes = document.Find("p");

// Get all HtmlElementNode nodes (tags) in the document
// Same result as not specifying the tag name
// Also the same result as document.FindOfType<HtmlElementNode>();
nodes = document.Find("*");
Specifying Attributes

There are several ways to search for nodes with specific attributes. You can use the pound (#), period (.) or colon (:) to specify a value for the id, class or type attribute, respectively.

// Get any nodes with the attribute id="center-ad"
IEnumerable<HtmlElementNode> nodes = document.Find("#center-ad");

// Get any <div> tags with the attribute class="align-right"
nodes = document.Find("div.align-right");

// Returns all <input> tags with the attribute type="button"
nodes = document.Find("input:button");

For greater control over attributes, you can use square brackets ([]). This is similar to specifying attributes in jQuery, but there are some differences. The first difference is that all the variations for finding a match at the start, middle or end are not supported by HtmlMonkey. However, to make up for this limitation, you can use the := operator to specify that the value is a regular expression and the code will match if the attribute value matches that regular expression.

// Get any <p> tags with the attribute id="center-ad"
IEnumerable<HtmlElementNode> nodes = document.Find("p[id=\"center-ad\"]");

// Get any <p> tags that have both attributes id="center-ad" and class="align-right"
// Quotes within the square brackets are optional if the value contains no whitespace or most punctuation.
nodes = document.Find("p[id=center-ad][class=align-right]");

// Returns all <a> tags that have an href attribute
// The value of that attribute does not matter
nodes = document.Find("a[href]");

// Get any <p> tags with the attribute data-id with a value that matches the regular
// expression "abc-\d+"
// Not case-sensitive
nodes = document.Find("p[data-id:=\"abc-\\d+\"]");

// Finds all <a> links that link to blackbeltcoder.com
// Uses a regular expression to allow optional http:// or https://, and www. prefix
// This example is also not case-sensitive
nodes = document.Find("a[href:=\"^(http:\\/\\/|https:\\/\\/)?(www\\.)?blackbeltcoder.com\"]");

Note that there is one key difference when using square brackets. When using a pound (#), period (.) or colon (:) to specify an attribute value, it is considered a match if it matches any value within that attribute. For example, the selector div.right-align would match the attribute class="main-content right-align". When using square brackets, it must match the entire value (although there are exceptions to this when using regular expressions).

Multiple Selectors

There are several cases where you can specify multiple selectors.

// Returns all <a>, <div> and <p> tags
IEnumerable<HtmlElementNode> nodes = document.Find("a, div, p");

// Returns all <span> tags that are descendants of a <div> tag
nodes = document.Find("div span");

// Returns all <span> tags that are a direct descendant of a <div> tag
nodes = document.Find("div > span");
Selector Performance

Obviously, there is some overhead parsing selectors. If you want to use the same selectors more than once, you can optimize your code by parsing the selectors into data structures and then passing those data structures to the find methods. The following code is further optimized by first finding a set of container nodes, and then potentially performing multiple searches against those container nodes.

// Parse selectors into SelectorCollections
SelectorCollection containerSelectors = Selector.ParseSelector("div.container");
SelectorCollection itemSelectors = Selector.ParseSelector("p.item");

// Search document for container nodes
IEnumerable<HtmlElementNode> containerNodes = containerSelectors.Find(document.RootNodes);

// Finally, search container nodes for item nodes
IEnumerable<HtmlElementNode> itemNodes = itemSelectors.Find(containerNodes);

Enhancing the Library

This is my initial attempt at this library and I would appreciate and be responsive to any feedback from people working with it. I want to keep the library small but would like to see more testing done on a wide variety of input markup. What sort of scenarios does the library not handle correctly? This is the type of information I'd be curious about.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net5.0

    • No dependencies.
  • net6.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on SoftCircuits.HtmlMonkey:

Package Downloads
SoftCircuits.WebScraper

.NET library to scrape content from the Internet. Use it to extract information from Web pages in your own application. Extracted data is written to a CSV file. Supports paging and can cycle through all combinations of any number of replacement tags. Now targets .NET Standard 2.0 or .NET 5.0, and supports nullable reference types.

TwemojiSharp

Unofficial C# wrapper of twemoji

Pillsgood.AdventOfCode

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.0 2,378 3/17/2024
2.1.3 274 2/11/2024
2.1.2 14,068 5/19/2022
2.1.1 898 5/7/2022
2.1.0 3,082 12/12/2021
2.0.7 953 8/29/2021
2.0.6 4,535 4/26/2021
2.0.5 829 3/26/2021
2.0.4 831 3/13/2021
2.0.3 1,543 2/22/2021
2.0.2 757 2/22/2021
2.0.1 762 2/21/2021
2.0.0 744 2/20/2021
1.3.1 1,117 11/1/2020
1.3.0 784 10/29/2020
1.2.1 954 10/18/2020
1.2.0 877 10/16/2020
1.1.5 902 10/4/2020
1.1.4 909 9/9/2020
1.1.3 1,163 8/13/2020
1.1.2 949 5/19/2020
1.1.1 1,490 5/10/2020
1.1.0 882 5/10/2020
1.0.4 920 2/26/2020
1.0.3 984 2/21/2020
1.0.2 3,079 7/16/2019
1.0.1 898 7/7/2019
1.0.0 932 7/2/2019

Added asynchronous file methods (.NET 5.0 and later only); Now also targeting .NET 6.0.