强大而灵活的的Html解析器——Html Agility Pack

一、概述

Html Agility Pack 简称HAP,是一个强大而灵活的解析Html DOM的.Net类库。

二、官方链接

官网:http://html-agility-pack.net/

NuGet: https://www.nuget.org/packages/HtmlAgilityPack/

Github:https://github.com/zzzprojects/html-agility-pack

三、用法及示例

1、解析前需要加载资源,有四种方式载入:

(1) File

var path = @"D:\test.html";
        
var doc = new HtmlDocument();
doc.Load(path);

以上的path是自己的文件路径,这样html被加载到了Doc之后就可以解析了。

(2) String

var html = @"<!DOCTYPE html>
<html>
<body>
	<h1>This is <b>bold</b> heading</h1>
	<p>This is <u>underlined</u> paragraph</p>
	<h2>This is <i>italic</i> heading</h2>
</body>
</html> ";

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

var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");

 以上的html是一个html格式的字符串。通过LoadHtml加载到了htmlDoc。在爬虫过程中通过url获取的html内容字符串后,就可以通过这种方式加载。

(3) Web

var html = @"http://html-agility-pack.net/";

HtmlWeb web = new HtmlWeb();

var htmlDoc = web.Load(html);

这个方法则是直接通过url加载。如果是只有url,则较之前一种方法更为简单,不需要再另写通过url获取html内容字符串的方法。

(4) Browser

string url = "http://html-agility-pack/from-browser";

var web1 = new HtmlWeb();
var doc1 = web1.LoadFromBrowser(url, o =>
{
    var webBrowser = (WebBrowser) o;

    // WAIT until the dynamic text is set
    return !string.IsNullOrEmpty(webBrowser.Document.GetElementById("uiDynamicText").InnerText);
});
var t1 = doc1.DocumentNode.SelectSingleNode("//div[@id='uiDynamicText']").InnerText;

var web2 = new HtmlWeb();
var doc2 = web2.LoadFromBrowser(url, html =>
{
    // WAIT until the dynamic text is set
    return !html.Contains("<div id=\"uiDynamicText\"></div>");
});
var t2 = doc2.DocumentNode.SelectSingleNode("//div[@id='uiDynamicText']").InnerText;

Console.WriteLine("Text 1: " + t1);
Console.WriteLine("Text 2: " + t2);

此方法需要在WinForm中使用。通过LoadFromBrowser方法来加载.

2、选择器

(1) SelectNodes(),选择与xPath表达式匹配的节点,如果不存在匹配的节点,则返回null.

示例1:返回匹配表达式“/td/input”的节点集合


var
htmlDoc = new HtmlDocument(); htmlDoc.LoadHtml(html);
string name = htmlDoc.DocumentNode
.SelectNodes("//td/input");

当需要只返回匹配的第一个节点时,用.first。

string name = htmlDoc.DocumentNode
    .SelectNodes("//td/input")
    .First()

用first的前提是这种节点要存在。否则返回的是null时不能再用first。因此可以先获取匹配的节点集合,判断不为空的情况下,再用first:

var inputNodes = htmlDoc.DocumentNode.SelectNodes("//td/input");
if (inputNodes!=null)
{
  string name = inputNodes.First();
}

示例2:通过class或者id等返回匹配节点,以下是为了获取class='news-item'的所有div节点。

var mainNode = htmlDoc.DocumentNode.SelectNodes(@"//div[@class='news-item']");

当然,div可以根据需要换成span、h1、img等节点。class也可以根据需要换成id或其他。

(2) SelectSingleNode 。获取第一个匹配Xpath表达式的节点。节点不存在则返回null。

示例1:

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

string name = htmlDoc.DocumentNode
    .SelectSingleNode("//td/input");

示例2:

var firstNode = htmlDoc.DocumentNode.SelectSingleNode(@"//div[@class='viewport']");

 (3)结合linq的使用更强大: 通过where方法或者firstordefault方法,以及节点的特性或者节点的内容等获取节点。

示例:

HtmlWeb web = new HtmlWeb();
            var doc = web.Load(model.Url);
            var textNode = doc.DocumentNode.Descendants("div").FirstOrDefault(m => m.GetAttributeValue("class", "") == "text" && m.GetAttributeValue("dir", "") == "ltr");

var tdNode = htmlDoc.DocumentNode.SelectNodes(@"//td[@colspan='3']").Where(n => n.InnerHtml.Contains("<a href=") && n.InnerHtml.Contains("<p align=")).FirstOrDefault();

 

3、通过以上方式获取节点后,就可以对节点进行操作了。

InnerHtml:获取或设置节点内的html。

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

var htmlNodes = htmlDoc.DocumentNode.SelectNodes("//body/h1");

foreach (var node in htmlNodes)
{
    Console.WriteLine(node.InnerHtml);
}

InnerText:获取或设置节点内的text。

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

var htmlNodes = htmlDoc.DocumentNode.SelectNodes("//body/h1");

foreach (var node in htmlNodes)
{
    Console.WriteLine(node.InnerText);
}

AppendChild:向指定节点的末尾增加子节点:

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

var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");
        
HtmlNode h2Node = HtmlNode.CreateNode("<h2> This is h2 heading</h2>");

htmlBody.AppendChild(h2Node);

Remove:移除节点

var htmlDoc = new HtmlDocument();

htmlDoc.LoadHtml(html);

var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");

HtmlNode node = htmlBody.ChildNodes[1];

node.Remove();
遍历

ChildNodes:获取所有子节点:

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

var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");
        
HtmlNodeCollection childNodes = htmlBody.ChildNodes;
        
foreach (var node in childNodes)
{
    if (node.NodeType == HtmlNodeType.Element)
    {
        Console.WriteLine(node.OuterHtml);
    }
}    

FirstChild:获取第一个子节点:

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

var htmlNodes = htmlDoc.DocumentNode.SelectNodes("//body");

var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");
        
HtmlNode firstChild = htmlBody.FirstChild;
        
Console.WriteLine(firstChild.OuterHtml);    

LastChild:获取最后一个子节点,示例同上。

Descendants():返回所有后代节点:

var node = htmlDoc.DocumentNode.SelectSingleNode("//body");

foreach (var nNode in node.Descendants())
{
    if (nNode.NodeType == HtmlNodeType.Element)
    {
        Console.WriteLine(nNode.Name);
    }
}

Descendants(String):获取指定名称的后代节点:

var node = htmlDoc.DocumentNode.SelectSingleNode("//body");

foreach (var nNode in node.Descendants("h2"))
{
    if (nNode.NodeType == HtmlNodeType.Element)
    {
        Console.WriteLine(nNode.Name);
    }
}

 

属性

SetAttributeVaule设置节点属性:

var h1Node = htmlDoc.DocumentNode.SelectSingleNode("//h1");
        
h1Node.Attributes.Append("style");
        
h1Node.SetAttributeValue("style", "color:blue");

GetAttributeVaule获取节点属性:

如下例子中常用来在爬虫中获取音视频的url

var source = htmlDoc.DocumentNode.SelectSingleNode(@"//source[@type='audio/mpeg']");
var audioUrl = source.GetAttributeValue("src", "");

如下示例常用来在爬虫中获取title

var title = childHtmlDoc.DocumentNode.SelectSingleNode(@"//input[@name='title']").GetAttributeValue("value", "");

 

posted @ 2017-12-04 11:52  向萧  阅读(1751)  评论(0编辑  收藏  举报