     [Blog](https://scrapfly.io/blog)   /  [css-selectors](https://scrapfly.io/blog/tag/css-selectors)   /  [HtmlAgilityPack vs AngleSharp: Which C# HTML Parser Should You Use?](https://scrapfly.io/blog/posts/htmlagilitypack-vs-anglesharp)   # HtmlAgilityPack vs AngleSharp: Which C# HTML Parser Should You Use?

 by [Ziad Shamndy](https://scrapfly.io/blog/author/ziad) Sep 11, 2026 16 min read [\#css-selectors](https://scrapfly.io/blog/tag/css-selectors) [\#data-parsing](https://scrapfly.io/blog/tag/data-parsing) [\#tools](https://scrapfly.io/blog/tag/tools) [\#xpath](https://scrapfly.io/blog/tag/xpath) 

 [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp "Share on LinkedIn") [  ](https://x.com/intent/tweet?url=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp&text=HtmlAgilityPack%20vs%20AngleSharp%3A%20Which%20C%23%20HTML%20Parser%20Should%20You%20Use%3F "Share on X") [  ](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp "Share on Facebook")    

 

 

Summarize this article with

 [  ](https://chat.openai.com/?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp) [  ](https://claude.ai/new?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp) [  ](https://x.com/i/grok?text=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp) [  ](https://www.perplexity.ai/search/new?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp) [  ](https://www.google.com/search?udm=50&aep=11&q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhtmlagilitypack-vs-anglesharp) 



         

A parser choice looks cosmetic on day one. Then a project grows to hundreds of extraction rules, and every selector, every malformed page, and every DOM quirk depends on whichever HTML parser got picked first.

This guide compares [Html Agility Pack](https://html-agility-pack.net/) and [AngleSharp](https://anglesharp.github.io/) for C# HTML parsing, using the same fetched HTML for both parsers. It covers XPath versus CSS selectors, malformed markup, DOM behavior, JavaScript boundaries, and migration cost.



## Key Takeaways

A short summary before the full comparison:

- Html Agility Pack fits a codebase already built around XPath queries and its own tolerant HTML parser.
- AngleSharp fits a new project that wants a standards based DOM and CSS selector queries instead.
- Both libraries parse HTML the application already retrieved. Neither library renders JavaScript by itself.
- Compare both parsers against the same input HTML and the same expected fields, not a general impression.
- Malformed markup can produce different DOM trees between the two parsers, even when extracted text still matches.
- Choose by selector model, DOM behavior, and migration cost, not by an unverified speed multiplier.

**Get web scraping tips in your inbox**Trusted by 100K+ developers and 30K+ enterprises. Unsubscribe anytime.







## HtmlAgilityPack vs AngleSharp: What Is the Short Answer?

Html Agility Pack is the safer choice for a codebase already built around XPath queries and tolerant parsing. AngleSharp is the cleaner choice for a new project that wants a standards based DOM and CSS selectors.

| Decision | Html Agility Pack | AngleSharp |
|---|---|---|
| Best fit | Existing XPath based code | New CSS selector or DOM first code |
| Selector model | XPath | CSS selectors, plus a DOM query API |
| DOM style | Library specific tree | Spec driven, browser like tree |
| Malformed HTML | Tolerant, custom recovery | HTML5 parsing algorithm |
| JavaScript | Not supported | Not supported in the core package |
| Typical migration cost | Lower for Html Agility Pack based projects | Lower for CSS first projects |

Parser correctness for one project depends on that project's own fixtures and expected output fields. A generic benchmark cannot substitute for testing both parsers against pages the application actually needs to extract.

The examples below fetch one HTML document and pass the same string to both parsers.



## How Do Html Agility Pack and AngleSharp Parse the Same HTML?

Both libraries install as NuGet packages.

The example below fetches one HTML string from a live product listing, then parses that same string with both libraries. Any output difference traces back to the parser, not the input.

shell```shell
dotnet add package HtmlAgilityPack
dotnet add package AngleSharp
```



These two commands add [Html Agility Pack](https://www.nuget.org/packages/HtmlAgilityPack/) and [AngleSharp](https://www.nuget.org/packages/AngleSharp/). NuGet lists 1.13.0 and 1.8.1 as their latest stable releases on September 11, 2026.

### Html Agility Pack: Parsing with XPath

Html Agility Pack loads raw HTML into an `HtmlDocument`, then queries that tree with XPath through `SelectNodes`. Readers new to XPath syntax may want the background before the code below.

[Parsing HTML with XpathIntroduction to xpath in the context of web-scraping. How to extract data from HTML documents using xpath, best practices and available tools.](https://scrapfly.io/blog/posts/parsing-html-with-xpath)

csharp```csharp
using HtmlAgilityPack;

var http = new HttpClient();
var html = await http.GetStringAsync("https://web-scraping.dev/products");

var document = new HtmlDocument();
document.LoadHtml(html);

var titles = (document.DocumentNode
        .SelectNodes("//div[contains(@class, 'product')]//h3/a")
        ?? Enumerable.Empty<HtmlNode>())
    .Select(node => node.InnerText.Trim())
    .ToArray();

var prices = (document.DocumentNode
        .SelectNodes("//div[contains(@class, 'product')]//div[@class='price']")
        ?? Enumerable.Empty<HtmlNode>())
    .Select(node => node.InnerText.Trim())
    .ToArray();

Console.WriteLine(string.Join(", ", titles));
Console.WriteLine(string.Join(", ", prices));
```



The first XPath expression walks from a product card down to the title link. The second walks to the exact price division, using `@class='price'` instead of `contains` to avoid matching `price-wrap` too.

### AngleSharp: Parsing with CSS Selectors

AngleSharp parses raw HTML into a browser like DOM, then queries that DOM with CSS selectors through `QuerySelectorAll`. Readers new to CSS selector syntax may want the background before the code below.

[Parsing HTML with CSS SelectorsIntroduction to using CSS selectors to parse web-scraped content. Best practices, available tools and common challenges by interactive examples.](https://scrapfly.io/blog/posts/parsing-html-with-css)

csharp```csharp
using AngleSharp.Html.Parser;

var http = new HttpClient();
var html = await http.GetStringAsync("https://web-scraping.dev/products");

var document = await new HtmlParser().ParseDocumentAsync(html);

var titles = document.QuerySelectorAll("div.product h3 a")
    .Select(node => node.TextContent.Trim())
    .ToArray();

var prices = document.QuerySelectorAll("div.product .price")
    .Select(node => node.TextContent.Trim())
    .ToArray();

Console.WriteLine(string.Join(", ", titles));
Console.WriteLine(string.Join(", ", prices));
```



`div.product` matches the class token `product` only, so it skips the surrounding `products` and `products-wrap` containers automatically. CSS class selectors match whole tokens, which sidesteps the substring trap XPath's `contains` allows.

### Confirming Both Parsers Return the Same Fields

Fetching once and parsing twice proves the two field arrays actually match, instead of trusting two separate runs against a page that might change between requests.

csharp```csharp
using HtmlAgilityPack;
using AngleSharp.Html.Parser;

var http = new HttpClient();
var html = await http.GetStringAsync("https://web-scraping.dev/products");

var hapDoc = new HtmlDocument();
hapDoc.LoadHtml(html);
var hapTitles = (hapDoc.DocumentNode.SelectNodes("//div[contains(@class, 'product')]//h3/a")
        ?? Enumerable.Empty<HtmlNode>())
    .Select(n => n.InnerText.Trim())
    .ToArray();

var angleDoc = await new HtmlParser().ParseDocumentAsync(html);
var angleTitles = angleDoc.QuerySelectorAll("div.product h3 a")
    .Select(n => n.TextContent.Trim())
    .ToArray();

Console.WriteLine(hapTitles.SequenceEqual(angleTitles)); // True
Console.WriteLine(string.Join(", ", hapTitles));
```



Box of Chocolate Candy, Dark Red Energy Potion, Teal Energy Potion, Red Energy Potion, and Blue Energy Potion, the five products on that live listing page.

Both libraries return the same five product titles from the current listing HTML.



## How Does XPath in Html Agility Pack Compare With CSS Selectors in AngleSharp?

XPath is stronger for axes, text nodes, and relative tree navigation. CSS selectors read shorter for ordinary element, class, and attribute queries. Both syntaxes can also hide the same substring trap in slightly different clothes.

On the same live listing, `//div[contains(@class, 'price')]` returns 10 nodes instead of 5, because the substring `price` also matches the wrapper class `price-wrap`. The exact match `//div[@class='price']` returns 5.

csharp```csharp
using HtmlAgilityPack;

var http = new HttpClient();
var html = await http.GetStringAsync("https://web-scraping.dev/products");

var document = new HtmlDocument();
document.LoadHtml(html);

var broad = document.DocumentNode.SelectNodes("//div[contains(@class, 'price')]");
var exact = document.DocumentNode.SelectNodes("//div[@class='price']");

Console.WriteLine(broad.Count); // 10
Console.WriteLine(exact.Count); // 5
```



[Ultimate XPath Cheatsheet for HTML Parsing in Web ScrapingUltimate companion for HTML parsing using XPath selectors. This cheatsheet contains all syntax explanations with interactive examples.](https://scrapfly.io/blog/posts/xpath-cheatsheet)

CSS selectors avoid that specific trap because a class selector like `.price` only matches a whole token. The same substring behavior still exists in CSS through the `[class*='price']` attribute selector, so the risk moves, it does not disappear.



[Ultimate CSS Selector Cheatsheet for Web Scraping and HTML ParsingCSS selectors is a powerful HTML querying protocol which is used by browsers to determine what HTML elements to style. It's also incredibly useful in HTML parsing when web scraping or processing HTML data, as the same queries can be used to select values as well.](https://scrapfly.io/blog/posts/css-selector-cheatsheet)

A short syntax reference covers the tasks that come up most often when porting selectors between the two libraries:

| Task | XPath (Html Agility Pack) | CSS (AngleSharp) |
|---|---|---|
| Any descendant element | `//div` | `div` |
| Exact class match | `//div[@class='price']` | `.price` |
| Safe class token match | `//div[contains(concat(' ', normalize-space(@class), ' '), ' price ')]` | `.price` |
| Broad substring match | `//div[contains(@class, 'price')]` | `[class*='price']` |
| Attribute presence | `//a[@href]` | `a[href]` |
| First matching element | `(//div[@class='product'])[1]` | `document.QuerySelector("div.product")` |
| Following sibling element | `//h3/following-sibling::div` | `h3 ~ div` |
| Element text content | `.InnerText` on the matched node | `.TextContent` on the matched node |

Namespace handling depends on the parser and content type. AngleSharp places ordinary HTML elements in the HTML namespace (`http://www.w3.org/1999/xhtml`) and embedded SVG elements in the SVG namespace. For XPath over XML or XHTML, [Microsoft's XPath and namespace documentation](https://learn.microsoft.com/en-us/dotnet/standard/data/xml/select-nodes-using-xpath-navigation) covers explicit namespace handling.

Migration cost grows with the number of selectors and with any code that depends on Html Agility Pack's DOM behavior. Benchmark both implementations against saved fixtures before deciding whether to port an existing scraper.

Selector portability also depends on the DOM tree each parser builds from malformed HTML.



## Which Parser Handles Malformed HTML Better?

Both parsers are built for real, imperfect HTML, not strict XML. Both can still repair the same broken markup into different DOM trees, so a project's own saved fixtures should decide which output is acceptable.

### Fixture One: An Unclosed List

This fixture drops both closing `</li>` tags. A missing closing tag is enough to test how each parser recovers the list structure.

csharp```csharp
using HtmlAgilityPack;
using AngleSharp.Html.Parser;

const string brokenList = @"
<ul class='stock-list'>
<li class='item'>Teal Energy Potion
<li class='item'>Box of Chocolate Candy
</ul>";

var hapDoc = new HtmlDocument();
hapDoc.LoadHtml(brokenList);
var hapItems = hapDoc.DocumentNode.SelectNodes("//li[@class='item']")
    .Select(n => n.InnerText.Trim())
    .ToArray();

var angleDoc = await new HtmlParser().ParseDocumentAsync(brokenList);
var angleItems = angleDoc.QuerySelectorAll("li.item")
    .Select(n => n.TextContent.Trim())
    .ToArray();

Console.WriteLine(hapItems.Length);                     // 2
Console.WriteLine(angleItems.Length);                   // 2
Console.WriteLine(hapItems.SequenceEqual(angleItems));  // True
```



Both parsers close each `<li>` automatically once the next one starts, and both return the same two item strings. This is the easy case, and it is not where the two libraries actually disagree.

### Fixture Two: A Table Without an Explicit tbody

Browsers add an implied `<tbody>` around bare `<tr>` rows, and that spec rule is where the two parsers actually split, not the unclosed tags themselves.

csharp```csharp
using HtmlAgilityPack;
using AngleSharp.Html.Parser;

const string brokenTable = @"
<table id='stock'>
<tr><td>Teal Energy Potion<td>4.99
<tr><td>Box of Chocolate Candy<td>24.99
</table>";

var hapDoc = new HtmlDocument();
hapDoc.LoadHtml(brokenTable);
var hapTable = hapDoc.DocumentNode.SelectSingleNode("//table[@id='stock']");
var hapChildren = hapTable.ChildNodes
    .Where(n => n.NodeType == HtmlNodeType.Element)
    .Select(n => n.Name);
Console.WriteLine(string.Join(",", hapChildren));       // tr,tr

var angleDoc = await new HtmlParser().ParseDocumentAsync(brokenTable);
var angleTable = angleDoc.QuerySelector("table#stock");
var angleChildren = angleTable.Children.Select(c => c.TagName.ToLower());
Console.WriteLine(string.Join(",", angleChildren));     // tbody
```



Html Agility Pack keeps both `tr` rows as direct children of `table`. AngleSharp wraps the same two rows inside an inserted `tbody`, matching how a real browser builds the DOM for this markup.



A query written as `table > tbody > tr` finds both rows in AngleSharp's tree and finds nothing in Html Agility Pack's tree. Querying `tr` anywhere below the table returns the same two rows in both.

Neither fixture makes one parser universally better. The unclosed list recovered identically in both trees. The table did not, because Html Agility Pack skips the implied `tbody` insertion that AngleSharp applies automatically.

The benchmark below measures parse and query work against the same fetched HTML.



Scrapfly

#### Extract structured data automatically?

Scrapfly's Extraction API uses AI to turn any webpage into structured data — no selectors needed.

[Try Free →](https://scrapfly.io/register)## Is AngleSharp Faster Than Html Agility Pack?

This guide will not publish a universal speed multiplier for either parser. Parse time depends on page size, selector complexity, allocation patterns, and the machine running the benchmark, so a single number misleads more than it helps.

Three separate costs hide inside any casual speed claim:

- Parse time covers turning a raw HTML string into a DOM either library can query.
- Selector time covers running one XPath expression or one CSS selector against that DOM.
- Allocation cost covers how much heap memory each parser and each query allocates per run.

The harness below measures combined parse-and-query wall time. It validates equal results and warms up both libraries first, but it does not isolate parsing, selector execution, or allocations. Use BenchmarkDotNet when those distinctions matter.

csharp```csharp
using HtmlAgilityPack;
using AngleSharp.Html.Parser;
using System.Diagnostics;

var http = new HttpClient();
var html = await http.GetStringAsync("https://web-scraping.dev/products");

const int iterations = 200;
var parser = new HtmlParser();

var hapCheck = new HtmlDocument();
hapCheck.LoadHtml(html);
var hapCount = hapCheck.DocumentNode.SelectNodes("//div[contains(@class, 'product')]//h3/a")?.Count ?? 0;
var angleCount = parser.ParseDocument(html).QuerySelectorAll("div.product h3 a").Length;

if (hapCount != angleCount)
{
    throw new InvalidOperationException("Parsers disagree on element count, fix the query before timing.");
}

new HtmlDocument().LoadHtml(html);   // warm up Html Agility Pack
parser.ParseDocument(html);          // warm up AngleSharp

var hapClock = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
    var doc = new HtmlDocument();
    doc.LoadHtml(html);
    _ = doc.DocumentNode.SelectNodes("//div[contains(@class, 'product')]//h3/a");
}
hapClock.Stop();

var angleClock = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
    var doc = parser.ParseDocument(html);
    _ = doc.QuerySelectorAll("div.product h3 a");
}
angleClock.Stop();

Console.WriteLine($"Html Agility Pack: {hapClock.ElapsedMilliseconds} ms for {iterations} runs");
Console.WriteLine($"AngleSharp: {angleClock.ElapsedMilliseconds} ms for {iterations} runs");
```



Run that harness on the actual pages a project needs, in Release configuration, on the hardware that will run it in production. Local results vary enough between machines and .NET versions that a printed number would go stale fast.

Parser speed does not help when the target data is absent from the response HTML. JavaScript-generated content needs a rendering layer before parsing.



## Can Html Agility Pack or AngleSharp Render JavaScript?

Neither Html Agility Pack nor AngleSharp replaces a browser. Both parse whatever HTML string they receive, nothing more.

Content a target page builds with client side JavaScript after the initial load never reaches either parser, unless something else renders that page first and hands over the finished HTML.

A product detail page that renders its review list from a client side template is a common example. The server response can leave that section empty until a real browser executes the page's own script.



Routing that kind of target to a real browser first is the standard fix. [Playwright for .NET](https://playwright.dev/dotnet/) drives Chromium, Firefox, or WebKit from C#, with the same automation model Scrapfly covers for Python and JavaScript.

[Web Scraping With Playwright in 2026: A Python GuideScrape JavaScript-rendered pages with Playwright and Python in 2026: locators, waiting, request interception, resource blocking, and concurrency.](https://scrapfly.io/blog/posts/web-scraping-with-playwright-and-python)

shell```shell
dotnet add package Microsoft.Playwright
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install chromium
```



Replace `net8.0` with the project's target framework when it differs.

csharp```csharp
using Microsoft.Playwright;
using AngleSharp.Html.Parser;

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();

await page.GotoAsync("https://web-scraping.dev/product/1");
await page.WaitForSelectorAsync("#reviews .review");

var renderedHtml = await page.ContentAsync();
await browser.CloseAsync();

var document = await new HtmlParser().ParseDocumentAsync(renderedHtml);
var reviewCount = document.QuerySelectorAll("#reviews .review").Length;

Console.WriteLine(reviewCount);
```



`WaitForSelectorAsync` pauses until the review list actually renders, and `ContentAsync` then returns the fully rendered page as one HTML string. That string works as input for AngleSharp or Html Agility Pack, same as any other fetched page.

`AngleSharp.Js` adds a Jint-based JavaScript engine to AngleSharp's DOM. With AngleSharp.Io and AngleSharp.Css, it can also load resources and expose a broader browser-like surface. It still does not provide Chromium's layout engine or Playwright's browser automation APIs.

After rendering, protected or geo-specific targets still need a retrieval layer that handles request delivery.



## When Should Retrieval Move to Scrapfly?

Both parsers start their work only after a usable HTML string already exists somewhere in memory. Getting that string from a protected, heavily rendered, or geographically restricted target is a separate problem from parsing it.



Scrapfly's [Web Scraping API](https://scrapfly.io/products/web-scraping-api) handles that retrieval step, including anti-bot bypass, proxy rotation, and JavaScript rendering, through one HTTP endpoint that any C# project can call directly.

csharp```csharp
using System.Text.Json;
using AngleSharp.Html.Parser;

var apiKey = Environment.GetEnvironmentVariable("SCRAPFLY_API_KEY")
    ?? throw new InvalidOperationException("Set SCRAPFLY_API_KEY before running.");
var endpoint =
    $"https://api.scrapfly.io/scrape?key={apiKey}&url=https://web-scraping.dev/products";

var http = new HttpClient();
var response = await http.GetStringAsync(endpoint);

using var payload = JsonDocument.Parse(response);
var html = payload.RootElement
    .GetProperty("result")
    .GetProperty("content")
    .GetString()
    ?? throw new InvalidOperationException("Scrapfly returned no result.content value.");

var document = await new HtmlParser().ParseDocumentAsync(html);
var titles = document.QuerySelectorAll("div.product h3 a")
    .Select(n => n.TextContent.Trim())
    .ToArray();

Console.WriteLine(string.Join(", ", titles));
```



The response arrives as JSON, with the fetched response body in `result.content`. This example does not enable `render_js`, so the content is not browser-rendered. That field feeds into AngleSharp or Html Agility Pack exactly like the direct `HttpClient` calls used earlier in this guide.

Scrapfly currently ships official SDKs for Python, TypeScript, Go, and Rust, with no dedicated C# SDK yet. The REST endpoint above works from plain `HttpClient` regardless, with no SDK dependency required.

[11 Best Web Scraping APIs, Libraries, and Crawlers for Developers in 2026Compare the best web scraping tools in 2026. Pipeline-based guide covering Scrapfly, BeautifulSoup, Playwright, Scrapy, and more for production scraping.](https://scrapfly.io/blog/posts/best-web-scraping-apis)

The FAQ below covers XPath support, CSS selectors in Html Agility Pack, and migration between the two parsers.



## FAQ

Is AngleSharp a replacement for Html Agility Pack?AngleSharp can take over for Html Agility Pack in many new parsing workflows. Existing production code built around XPath queries and Html Agility Pack's own DOM shape still represents real migration work worth planning for.







Does AngleSharp support XPath?AngleSharp's core package favors DOM traversal and CSS selectors, with no built-in XPath engine. [AngleSharp.XPath](https://www.nuget.org/packages/AngleSharp.XPath/) is a separate extension maintained under the AngleSharp GitHub organization that adds XPath querying on top of AngleSharp's DOM.







Can Html Agility Pack use CSS selectors?Html Agility Pack's built in selector model is XPath only. [Fizzler](https://www.nuget.org/packages/Fizzler.Systems.HtmlAgilityPack/) once added CSS style `QuerySelector` support for Html Agility Pack, but that package has not shipped a release since 2020.







Which parser handles malformed HTML better?Neither parser wins every malformed page. Html Agility Pack and AngleSharp can repair the same broken markup into different DOM trees, so a project's own saved fixtures should decide which output is acceptable.









## Summary

Html Agility Pack remains the safer pick for a codebase already invested in XPath queries and its own tolerant parser. AngleSharp fits a new project that wants a standards based DOM and CSS selectors from the start.

Test both parsers against the same saved HTML and the same expected fields before committing an entire codebase to either one. That comparison, not general reputation, is what should decide the migration.

When the harder problem is retrieving usable HTML from a protected or heavily rendered target, Scrapfly's Web Scraping API handles that retrieval step so either parser can take over from a plain HTML string.



Legal Disclaimer and PrecautionsThis tutorial covers popular web scraping techniques for education. Interacting with public servers requires diligence and respect:

- Do not scrape at rates that could damage the website.
- Do not scrape data that's not available publicly.
- Do not store PII of EU citizens protected by GDPR.
- Do not repurpose *entire* public datasets which can be illegal in some countries.

Scrapfly does not offer legal advice but these are good general rules to follow. For more you should consult a lawyer.

 

   [  Add as a preferred source ](https://google.com/preferences/source?q=scrapfly.io) Table of Contents















 

  Table of Contents- [Key Takeaways](#key-takeaways)
- [HtmlAgilityPack vs AngleSharp: What Is the Short Answer?](#htmlagilitypack-vs-anglesharp-what-is-the-short-answer)
- [How Do Html Agility Pack and AngleSharp Parse the Same HTML?](#how-do-html-agility-pack-and-anglesharp-parse-the-same-html)
- [Html Agility Pack: Parsing with XPath](#html-agility-pack-parsing-with-xpath)
- [AngleSharp: Parsing with CSS Selectors](#anglesharp-parsing-with-css-selectors)
- [Confirming Both Parsers Return the Same Fields](#confirming-both-parsers-return-the-same-fields)
- [How Does XPath in Html Agility Pack Compare With CSS Selectors in AngleSharp?](#how-does-xpath-in-html-agility-pack-compare-with-css-selectors-in-anglesharp)
- [Which Parser Handles Malformed HTML Better?](#which-parser-handles-malformed-html-better)
- [Fixture One: An Unclosed List](#fixture-one-an-unclosed-list)
- [Fixture Two: A Table Without an Explicit tbody](#fixture-two-a-table-without-an-explicit-tbody)
- [Is AngleSharp Faster Than Html Agility Pack?](#is-anglesharp-faster-than-html-agility-pack)
- [Can Html Agility Pack or AngleSharp Render JavaScript?](#can-html-agility-pack-or-anglesharp-render-javascript)
- [When Should Retrieval Move to Scrapfly?](#when-should-retrieval-move-to-scrapfly)
- [FAQ](#faq)
- [Summary](#summary)
 
    Join the Newsletter  Get monthly web scraping insights 

 

  



Scale Your Web Scraping

Anti-bot bypass, browser rendering, and rotating proxies, all in one API. Start with 1,000 free credits.

  No credit card required  1,000 free API credits  Anti-bot bypass included 

 [Start Free](https://scrapfly.io/register) [View Docs](https://scrapfly.io/docs/onboarding) 

 Not ready? Get our newsletter instead. 

 

 ## Related Articles

 [  

 data-parsing xpath 

### Ultimate XPath Cheatsheet for HTML Parsing in Web Scraping

Ultimate companion for HTML parsing using XPath selectors. This cheatsheet contains all syntax explanations with interac...

 

 ](https://scrapfly.io/blog/posts/xpath-cheatsheet) [  

 python data-parsing 

### Parsing HTML with Xpath

Introduction to xpath in the context of web-scraping. How to extract data from HTML documents using xpath, best practice...

 

 ](https://scrapfly.io/blog/posts/parsing-html-with-xpath) [  

 data-parsing css-selectors 

### Parsing HTML with CSS Selectors

Introduction to using CSS selectors to parse web-scraped content. Best practices, available tools and common challenges ...

 

 ](https://scrapfly.io/blog/posts/parsing-html-with-css) 

  ## Related Questions

- [ Q XPath vs CSS selectors: what's the difference? ](https://scrapfly.io/blog/answers/xpath-vs-css-selectors)
- [ Q How to find HTML elements by class? ](https://scrapfly.io/blog/answers/how-to-find-html-elements-by-class)
- [ Q How to find elements by XPath in Puppeteer? ](https://scrapfly.io/blog/answers/how-to-find-elements-by-xpath-in-puppeteer)
- [ Q How to use CSS selectors in NodeJS when web scraping? ](https://scrapfly.io/blog/answers/how-to-use-css-selectors-in-nodejs)
 
  



   



 Extract structured data with AI, **1,000 free credits** [Start Free](https://scrapfly.io/register)