唐朝程序员

我来自唐朝

some things

////html[1]/body[1]/div[9]/div[1]/div[1]/div[1]/ul/li  截取规则
Articles/Article[1]:选取属于Articles子元素的第一个Article元素。 

/Articles/Article[last()]:选取属于Articles子元素的最后一个Article元素。 
/Articles/Article[last()-1]:选取属于Articles子元素的倒数第二个Article元素。 
/Articles/Article[position()<3]:选取最前面的两个属于 bookstore 元素的子元素的Article元素。 
//title[@lang]:选取所有拥有名为lang的属性的title元素。 
//CreateAt[@type='zh-cn']:选取所有CreateAt元素,且这些元素拥有值为zh-cn的type属性。 
/Articles/Article[Order>2]:选取Articles元素的所有Article元素,且其中的Order元素的值须大于2。 
/Articles/Article[Order<3]/Title:选取Articles元素中的Article元素的所有Title元素,且其中的Order元素的值须小于3。

刚刚学习了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    所有祖先节点

//删除注释,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())
    {
        
    }

 

      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  或和与

 

补充:

  多个属性条件查询      //div[@align='center' and @height='24']

  不存在class属性       //div[not(@class)]

static void Main(string[] args)
        {
            //<ul class="user_match clear">
            //    <li>年龄:21~30之间</li>
            //    <li>婚史:未婚</li>
            //    <li>地区:不限</li>
            //    <li>身高:175~185厘米之间</li>
            //    <li>学历:不限</li>
            //    <li>职业:不限</li>
            //    <li>月薪:不限</li>
            //    <li>住房:不限</li>
            //    <li>购车:不限</li>
            //</ul>


            WebClient wc = new WebClient();
            wc.BaseAddress = "http://www.juedui100.com/";
            wc.Encoding = Encoding.UTF8;
            HtmlDocument doc = new HtmlDocument();
            string html = wc.DownloadString("user/6971070.html");
            doc.LoadHtml(html);
            HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]");     //根据XPath查找节点,跟XmlNode差不多

            IEnumerable<HtmlNode> nodeList = node.Ancestors();  //获取该元素所有的父节点的集合
            foreach (HtmlNode item in nodeList)
            {
                Console.Write(item.Name + " ");   //输出 div div body html #document
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList1 = node.Ancestors("body");  //获取名字匹配的该元素的父集合,其实参数就是一个筛选的功能
            foreach (HtmlNode item in nodeList1)
            {
                Console.Write(item.Name + " ");   //输出 body
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList2 = node.AncestorsAndSelf();  //获取所有的父节点和自身
            foreach (HtmlNode item in nodeList2)
            {
                Console.Write(item.Name + " "); //输出 ul div div div body html #document
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList3 = node.AncestorsAndSelf("div");     //获取父节点和自身,参数用于筛选
            foreach (HtmlNode item in nodeList3)
            {
                Console.Write(item.Name + " "); //输出 div div div
            }
            Console.WriteLine();

            HtmlNode node1 = doc.CreateElement("li");
            node1.InnerHtml = "我是附加的li元素";
            node.AppendChild(node1);    //...<li>购车:不限</li> 后面加了一个<li>我是附加的li元素</li>
            Console.WriteLine(node.InnerHtml);


            HtmlNode node2 = doc.CreateElement("li");
            node2.InnerHtml = "新li一";
            HtmlNode node3 = doc.CreateElement("li");
            node3.InnerHtml = "新li二";
            HtmlNodeCollection nc = new HtmlNodeCollection(node2);
            nc.Add(node2);
            nc.Add(node3);
            node.AppendChildren(nc);    //一次过追加多个元素
            Console.WriteLine(node.InnerHtml);      //...<li>我是附加的li元素</li><li>新li一</li><li>新li二</li>

            Console.WriteLine(HtmlNode.CanOverlapElement("node2"));     //输出False   确定是否可以保存一个重复的元素

            IEnumerable<HtmlAttribute> attrs = node.ChildAttributes("class");   //获取子节点与自身的所有名为class的属性集合
            foreach (HtmlAttribute attr in attrs)
            {
                Console.Write(attr.Value);      //输出 user_match clear 
            }

            HtmlNode node4 = node.Clone();
            Console.WriteLine(node4.InnerHtml);     //输出node的代码,node已被复制到了node

            HtmlNode node5 = node.CloneNode(false); //参数决定是否复制子节点,与XmlNode一样
            Console.WriteLine(node5.OuterHtml);     //<ul class="user_match clear"></ul>    因为参数设为了false子节点没有被复制

            HtmlNode node6 = node.CloneNode("div");    //复制节点的同时,更改名字
            Console.WriteLine(node6.OuterHtml);        //输出 <div class="user_match clear"><li>年龄:21~30之间</li>...</div>  ul已被改为了div

            HtmlNode node7 = node.CloneNode("table",false);
            Console.WriteLine(node7.OuterHtml);        //输出<table class="user_match clear"></table>     参数为false所以没有复制子节点

            HtmlNode node8 = node.SelectSingleNode("child::li[1]");
            node.CopyFrom(node);
            Console.WriteLine(node.OuterHtml);
            Console.WriteLine("========================");
            //public void CopyFrom(HtmlNode node);
            //public void CopyFrom(HtmlNode node, bool deep);
            //public XPathNavigator CreateNavigator();
            //public XPathNavigator CreateRootNavigator();

            HtmlNode node9 = HtmlNode.CreateNode("<li>新节点</li>");   //直接用字符串创建节点,还是挺好用的
            Console.WriteLine(node9.OuterHtml);     //输出 <li>新节点</li>

            IEnumerable<HtmlNode> nodeList4 = node.DescendantNodes();   //获取所有的子节点集合
            foreach (HtmlNode item in nodeList4)
            {
                Console.Write(item.OuterHtml);      //输出 node的每个子li节点
            }
            Console.WriteLine("===================");

            IEnumerable<HtmlNode> nodeList5 = node.DescendantNodesAndSelf();
            foreach (HtmlNode item in nodeList5)
            {
                Console.Write(item.OuterHtml);      //输出自身<ul>..包括子节点<li>...</li></ul> 再输出所有的子li节点
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList6 = node.DescendantNodes();   //获取枚举列表中的所有子代节
            foreach (HtmlNode item in nodeList6)
            {
                Console.Write(item.InnerText);  //输出所有的li节点的内容
            }
            Console.WriteLine("---------------");

            IEnumerable<HtmlNode> nodeList7 = node.Descendants("li");   //获取所有的子后代元素    //文本节点不在此范围内
            foreach(HtmlNode item in nodeList7)
            {
                Console.Write(item.InnerText);   
            }

            IEnumerable<HtmlNode> nodeList8 = node.DescendantsAndSelf("ul");   //获取所有的子后代元素    //文本节点不在此范围内
            foreach (HtmlNode item in nodeList8)
            {
                Console.Write(item.Name);       //输出 ul 参数实际上只相当于过滤的作用
            }

            HtmlNode node10 = node.Element("li");   //获取第一个子节点名称匹配的元素
            Console.WriteLine(node10.InnerText);        //输出 年龄:年龄:21~30之间
            Console.WriteLine("----------------------------------------");

            IEnumerable<HtmlNode> nodeList9 = node.Elements("li");
            foreach (HtmlNode item in nodeList9)
            {
                Console.Write(item.InnerText);      //输出 所有的li节点内容
            }
            Console.WriteLine();

            //换一个新的,好像有点乱了
            HtmlNode newnode = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[3]");
            //<div class="col say">
            //    <h3>爱情独白</h3>
            //    <p>愿得一心人,白首不相离。我一直相信我的另一半就在茫茫人海中,有一天一定会与我相遇。</p>
            //</div>

            //bool b = newnode.GetAttributeValue("class", false);   //获取一个布尔值的属性,没有找到则返回第二个参数的默认值
            //Console.WriteLine(b);
            //int i = newnode.GetAttributeValue("class", 0);        //获取一个整形的属性,没有找到则返回第二个参数的默认值
            //Console.WriteLine(i);

            string str = newnode.GetAttributeValue("class", "");    //获取一个字符串属性
            Console.WriteLine(str); //输出 col say

            HtmlNode node11 = HtmlNode.CreateNode("<b>我是加粗节点</b>");
            HtmlNode node12 = newnode.SelectSingleNode("h3");
            newnode.InsertAfter(node11, node12);    //意思是在node12代表的h3节点后面插入node11节点
            Console.WriteLine(newnode.InnerHtml);   //h3>爱情独白</h3><b>我是加粗节点</b><p>愿得一心人...      留意到b节点已经被插入到h3后面

            newnode.InsertBefore(node11, node12);   //再插入多一次,方法不同罢了,这次是在node12带包的h3前面插入
            Console.WriteLine(newnode.InnerHtml);   //<b>我是加粗节点</b><h3>爱情独白</h3><b>我是加粗节点</b><p>愿得一心人

            Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            newnode.RemoveChild(node11);    //移除了第一个<b>我是加粗节点</b>   此方法的重载,第二个参数决定是否移除孙子节点
            Console.WriteLine(newnode.InnerHtml);   //<h3>爱情独白</h3><b>我是加粗节点</b><p>愿得一心人....

            newnode.RemoveAllChildren();        //移除所有子节点
            Console.WriteLine(newnode.OuterHtml);   //<div class="col say"></div>   所有子节点都被移除了

            newnode.RemoveAll();                    //移除所有的属性和子节点,由于子节点已经被上个方法移除了,因此这次连属性也移除了
            Console.WriteLine(newnode.OuterHtml);   //输出 <div></div>    注意到属性也被移除了。

            //都移除光了,再来一个,还是刚才那个
            HtmlNode newnode1 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[3]");
            Console.WriteLine("===================");
            Console.WriteLine(newnode1.OuterHtml);  //输出 <div></div>    注意 移除是从HtmlDocument中移除的,再次获取获取不到了

            HtmlNode newnode2 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/div[2]/p");
            Console.WriteLine(newnode2.OuterHtml);
            //<p class="no_tip">她还没有设置不能忍受清单 
            //    <a href="javascript:invite(5971070,8,'邀请设置不能忍受');" class="link_b needlogin">邀请她设置</a>
            //</p>
            newnode2.Remove();    //从文档树中移除newnode2节点
            HtmlNode newnode3 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/div[2]/p");   //再次获取该节点
            //Console.WriteLine(newnode3.OuterHtml);  //报未将对象引用到对象的实例异常,明显是找不到了,

            HtmlNode newnode4 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[1]/div/div[1]/p[2]/b[1]");
            Console.WriteLine(newnode4.OuterHtml);
            //<b>相册:
            //    <a href="/photo/6971070.html" class="red">4</a>张
            //</b>
            HtmlNode node17 = HtmlNode.CreateNode("<div>再次创建一个节点</div>");
            newnode4.PrependChild(node17);      //跟AppengChild类似,只是插入位置不同PrependChildren接受一个节点集合,一次过插入多个节点而已
            Console.WriteLine(newnode4.OuterHtml);  
            //输出
            //<b>相册:
            //    <div>再次创建一个节点</div>
            //    <a href="/photo/6971070.html" class="red">4</a>张
            //</b>
            HtmlNode node16 = newnode4.SelectSingleNode("child::a[1]");
            HtmlNode node18 = HtmlNode.CreateNode("<p>新建一行</p>");
            newnode4.ReplaceChild(node18, node16);
            Console.WriteLine(newnode4.OuterHtml);
            //输出
            //<b>相册:
            //    <div>再次创建一个节点</div>
            //    <p>新建一行</p>张      //留意到node16代表得节点已经被替换掉了
            //</b>

            HtmlNode node19 = newnode4.SelectSingleNode("child::p[1]");
            node19.SetAttributeValue("class","class1");
            Console.WriteLine(node19.OuterHtml);    //输出 <p class="class1">新建一行</p>

            Console.WriteLine(HtmlNode.IsOverlappedClosingElement("<a>我爱你</a>"));   //输出 False
            Console.WriteLine(HtmlNode.IsCDataElement("<a>我爱你</a>"));   //输出 False
            Console.WriteLine(HtmlNode.IsClosedElement("<a>我爱你</a>"));   //输出 False
            Console.WriteLine(HtmlNode.IsEmptyElement("<a>我爱你</a>"));   //输出 False
            Console.WriteLine(newnode4.OuterHtml);


            HtmlNode node20 = HtmlNode.CreateNode("<p>新的第二行</p>");
            newnode4.AppendChild(node20);
            HtmlNodeCollection hnc = newnode4.SelectNodes("//p");   //根据XPath一次过获取多个Node
            Console.WriteLine(hnc.Count);   //输出29

            string str1 = node20.WriteContentTo();
            Console.WriteLine(str1);    //输出 新的第二行  将节点内容写入字符串

            //public void WriteContentTo(TextWriter outText);
            //public string WriteTo();
            //public void WriteTo(TextWriter outText);
            //public void WriteTo(XmlWriter writer);
            Console.ReadKey();
        }

 

 

var divs = html.CssSelect("div");  //all div elements

var nodes = html.CssSelect("div.content"); //all div elements with css class ‘content’

var nodes = html.CssSelect("div.widget.monthlist"); //all div elements with the both css class

var nodes = html.CssSelect("#postPaging"); //all HTML elements with the id postPaging

var nodes = html.CssSelect("div#postPaging.testClass"); // all HTML elements with the id postPaging and css class testClass 

var nodes = html.CssSelect("div.content > p.para"); //p elements who are direct children of div elements with css class ‘content’ 

var nodes = html.CssSelect("input[type=text].login"); // textbox with css class login 

We can also select ancestors of elements:

var nodes = html.CssSelect("p.para").CssSelectAncestors("div.content > div.widget");

 

常用函数

xpath的常用函数主要包含节点集函数,字符串函数,布尔函数,数字函数,网上的资料较多,在此就不再累述,可参考以下资料:

[a] XPath, XQuery, and XSLT Functions http://www.w3schools.com/xpath/xpath_functions.asp

[b] XPath Functions http://www.caucho.com/resin-3.0/xml/xpath-fun.xtp

[c] XPath Functions(MSDN) http://msdn2.microsoft.com/en-us/library/ms256138.aspx

常用定位语句实例

1. //NODE[not(@class)] 所有节点名为node,且不包含class属性的节点

2. //NODE[@class and @id] 所有节点名为node,且同时包含class属性和id属性的节点

3. //NODE[contains(text(),substring] 所有节点名为node,且其文本中包含substring的节点

//A[contains(text(),\"下一页\")] 所有包含“下一页”字符串的超链接节点

//A[contains(@title,"文章标题")] 所有其title属性中包含“文章标题”字符串的超链接节点

4. //NODE[@id="myid"]/text() 节点名为node,且属性id为myid的节点的所有直接text子节点

5. BOOK[author/degree] 所有包含author节点同时该author节点至少含有一个的degree孩子节点的book节点

6. AUTHOR[.="Matthew Bob"] 所有值为“Matthew Bob”的author节点

7. //*[count(BBB)=2] 所有包含两个BBB孩子节点的节点

8. //*[count(*)=2] 所有包含两个孩子节点的节点

9. //*[name()='BBB'] 所有名字为BBB的节点,等同于//BBB

10. //*[starts-with(name(),'B')] 所有名字开头为字母B的节点

11. //*[contains(name(),'C')] 所有名字中包含字母C的节点

12. //*[string-length(name()) = 3] 名字长度为3个字母的节点

13. //CCC | //BBB 所有CCC节点或BBB节点

14. /child::AAA 等价于/AAA

15. //CCC/descendant::* 所有以CCC为其祖先的节点

16. //DDD/parent::* DDD节点的所有父节点

17. //BBB[position() mod 2 = 0] 偶数位置的BBB节点

18. AUTHOR[not(last-name = "Bob")] 所有不包含元素last-name的值为Bob的节点

19. P/text()[2] 当前上下文节点中的P节点的第二个文本节点

20. ancestor::BOOK[1] 离当前上下文节点最近的book祖先节点

21. //A[text()="next"] 锚文本内容等于next的A节点

 

 

最后推荐一款在Firefox中用的XPath插件:

XPath Checker

https://addons.mozilla.org/en-US/firefox/addon/1095

这个插件可以方便查看网页中任意元素的XPath路径,但其自动生成的XPath路径通常不是最简路径。

 

参考资料

[1]XPath Examples. http://msdn2.microsoft.com/en-us/library/ms256086.aspx

[2]XPath Tutorial http://www.zvon.org/xxl/XPathTutorial/Output/example1.html

[3]XPath介绍 http://www.xml.org.cn/dispbbs.asp?boardID=14&ID=35493

[4]XPath reference http://msdn2.microsoft.com/en-us/library/ms256115.aspx

[5]XML Path Language (XPath)Version 1.0 http://www.w3.org/TR/xpath

[6]XPath Tutorial http://www.w3schools.com/xpath/default.asp

posted on 2014-05-23 12:57  唐朝程序员  阅读(513)  评论(0编辑  收藏  举报

导航