Scraping a Web Page in C# with HtmlAgilityPack
Short answer
How do you scrape a web page in C# with HtmlAgilityPack?
Install the HtmlAgilityPack NuGet package, download the page HTML with HttpClient, load it into an HtmlDocument and select nodes with XPath through SelectNodes or SelectSingleNode. For JavaScript-rendered pages, fetch the rendered HTML from an API such as AlterLab first, then parse it with HtmlAgilityPack exactly the same way.
Step by step
- 1
Install the package
Run `dotnet add package HtmlAgilityPack` in your project.
- 2
Download the HTML
Use a shared HttpClient instance to get the page as a string. Reuse the client instead of creating one per request.
- 3
Load and query
Call `doc.LoadHtml(html)` and select elements with XPath, for example `//h2[@class='title']`.
- 4
Handle missing nodes
SelectNodes returns null when nothing matches, so check for null before iterating.
Example
using HtmlAgilityPack;
var http = new HttpClient();
var html = await http.GetStringAsync("https://example.com/articles");
var doc = new HtmlDocument();
doc.LoadHtml(html);
var titles = doc.DocumentNode.SelectNodes("//h2[@class='title']");
if (titles != null)
{
foreach (var node in titles)
Console.WriteLine(node.InnerText.Trim());
}Frequently Asked Questions
Can HtmlAgilityPack run JavaScript?
No. It only parses the HTML you give it. If the content is built by JavaScript, fetch the rendered HTML first, for example through AlterLab with render_js enabled, and parse that.
Does HtmlAgilityPack support CSS selectors?
Not natively; it uses XPath. Add the Fizzler extension for CSS selectors, or switch to AngleSharp if you prefer a CSS-first API.
Read the full guide
C# web scraping guideTry it with AlterLab
Send a URL, get the page back as HTML, markdown or JSON. Rendering, proxy rotation and retries are handled for you.
Your first scrape.
Sixty seconds.
Up to 5,000 free requests — about 5,000 Light, 500 Protected or 250 Dynamic requests. No credit card.
Just a POST request.
No credit card required · Up to 5,000 free requests (about 5,000 Light, 500 Protected or 250 Dynamic requests) · Balance never expires