当我们需要解析一个web页面的时候,如果非常简单,可以用字符串查找的方式,复杂一点可以用正则表达式,但是有时候正则很麻烦的,因为html代码本身就比较麻烦,像常用的img标签,这个东东到了浏览器上就没了闭合标签(一直还没搞懂为什么),想用XML解析,也是同样的原因根本解析不了,今天发现一个解析html控件,用了一下,非常好用。
这个控件叫做Html Agility Pack,主页在这儿:http://htmlagilitypack.codeplex.com/
这儿还有一篇blog介绍怎么使用的 (English):http://olussier.net/2010/03/30/easily-parse-html-documents-in-csharp/
我直接把例子贴这儿,一看就明白。因为是作为xml解析的,所以呢,少不了XPath,如果不懂这个东西的话,赶紧看看吧,现在xpath语法都扩展到css里面了,语法比较简单,先看看基础的就行了。
最基本的使用方法不是SelectSingleNode,而是GetElementById,这是与XmlDocument不同的地方。
// The HtmlWeb class is a utility class to get the HTML over HTTP HtmlWeb htmlWeb = new HtmlWeb(); // Creates an HtmlDocument object from an URL HtmlAgilityPack.HtmlDocument document = htmlWeb.Load("http://www.somewebsite.com"); // Targets a specific node HtmlNode someNode = document.GetElementbyId("mynode"); // If there is no node with that Id, someNode will be null if (someNode != null) { // Extracts all links within that node IEnumerable<htmlnode> allLinks = someNode.Descendants("a"); // Outputs the href for external links foreach (HtmlNode link in allLinks) { // Checks whether the link contains an HREF attribute if (link.Attributes.Contains("href")) { // Simple check: if the href begins with "http://", prints it out if (link.Attributes["href"].Value.StartsWith("http://")) Console.WriteLine(link.Attributes["href"].Value); } } }</htmlnode>
使用xpath
// Extracts all links under a specific node that have an href that begins with "http://" HtmlNodeCollection allLinks = document.DocumentNode.SelectNodes("//*[@id='mynode']//a[starts-with(@href,'http://')]"); // Outputs the href for external links foreach (HtmlNode link in allLinks) Console.WriteLine(link.Attributes["href"].Value);
One more
path = "//table[@id='1' or @id='2' or @id='3']//a[@onmousedown]"; xpath = "//ul[@id='wg0']//li[position()<4]/h3/a"; xpath = "//div[@class='resitem' and position()<4]/a"; xpath = "//li[@class='result' and position()<4]/a";
使用方法:
刚刚学习了XPath路径表达式,主要是对XML文档中的节点进行搜索,通过XPath表达式可以对XML文档中的节点位置进行快速定位和访问,html也是也是一种类似于xml的标记语言,但是语法没有那么严谨,在codeplex里有一个开源项目HtmlAgilityPack,提供了用XPath解析HTML文件,下面掩饰如何使用该类库的使用
首先说下XPath路径表达式
XPath路径表达式
用来选取XML文档中的节点或节点集的
1、术语:节点(Node):7种类型:元素,属性,文本,命名空间,处理命令,注释,文档(根)节点
2、节点关系:父(Parent),子(Children),同胞(Sibling),先辈(Ancestor),后代(Descendant)
3、路径表达式
nodename 节点名,选取此节点的所有子节点 例: childnode 当前节点中的childnode子节点,不包含孙子及以下的节点
/ 从根节点选取 例:/root/childnode/grandsonnode
// 表示所有后代节点 例://childnode 所有名为childnode的后代节点
. 表示当前节点 例: ./childnode 表示当前节点的childnode节点
.. 表示父节点 例: ../nearnode 表示父亲节点的nearnode子节点
@ 选取属性 /root/childnode/@id 表示childnode的所有含有id属性的节点集
4、谓语(Predicates)
谓语可以对节点集进行一些限制,使选择更精确
/root/book[1] 节点集中的第一个节点
/root/book[last()] 节点集中最后一个节点
/root/book[position() - 1] 节点集中倒数第二个节点集
/root/book[position() < 5] 节点集中前五个节点集
/root/book[@id] 节点集中含有属性id的节点集
/root/book[@id='chinese'] 节点集中id属性值为chinese的节点集
/root/book[price > 35]/title 节点集中book的price元素值大于35的title节点集
5、通配符:XPath路径中同样支持通配符(*,@*,node(), text())
例: /bookstore/*
//title[@*]
6、XPath轴
定义相对于当前节点的节点集
ancestor 所有祖先节点
attribute 所有属性节点
child 所有子元素
descendant 所有后代节点(子,孙。。。)
following 结束标记后的所有节点 preceding 开始标记前的所有节点
following-sibling 结束标记后的所有同胞节点
preceding-sibling 开始标记前的所有同胞节点
namespace 当前命名空间的所有节点
parent 父节点
self 当前节点
用法:轴名称::节点测试[谓语]
例: ancestor::book
child::text()
7、运算符
| 两个节点集的合并 例:/root/book[1] | /root/book[3]
+,-,*,dev,mod
=,!=,<,>,<=,>=
or,and 或和与
//删除注释,script,style node.Descendants() .Where(n => n.Name == "script" || n.Name == "style" || n.Name=="#comment") .ToList().ForEach(n => n.Remove()); //遍历node节点的所有后代节点 foreach(var HtmlNode in node.Descendants()) { }
HtmlAgilityPack类库用法
1、首先需要获取到html页面数据,可以通过WebRequest类来获取
public static string GetHtmlStr(string url) { try { WebRequest rGet = WebRequest.Create(url); WebResponse rSet = rGet.GetResponse(); Stream s = rSet.GetResponseStream(); StreamReader reader = new StreamReader(s, Encoding.UTF8); return reader.ReadToEnd(); } catch (WebException) { //连接失败 return null; } }
2、通过HtmlDocument类加载html数据
string htmlstr = GetHtmlStr("http://www.hao123.com"); HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(htmlstr); HtmlNode rootnode = doc.DocumentNode; //XPath路径表达式,这里表示选取所有span节点中的font最后一个子节点,其中span节点的class属性值为num //根据网页的内容设置XPath路径表达式 string xpathstring = "//span[@class='num']/font[last()]"; HtmlNodeCollection aa = rootnode.SelectNodes(xpathstring); //所有找到的节点都是一个集合 if(aa != null) { string innertext = aa[0].InnerText; string color = aa[0].GetAttributeValue("color", ""); //获取color属性,第二个参数为默认值 //其他属性大家自己尝试 }
也可以通过HtmlWeb类来获得HtmlDocument
HtmlWeb web = new HtmlWeb(); HtmlAgilityPack.HtmlDocument doc = web.Load(url); HtmlNode rootnode = doc.DocumentNode;
补充:
多个属性条件查询 //div[@align='center' and @height='24']
不存在class属性 //div[not(@class)]