使用C#和HtmlAgilityPack解析HTML

王明輝發表於2017-07-25

  近期,有一個需求,需要解析HTML頁面,讀取一些需要的資料後,插入本地資料庫。我知道可以通過正規表示式實現,然而正規表示式之於我,就像組合語言之於我,一樣。我知道它是幹什麼的,我也知道它能幹什麼,但是我一直不知道怎麼幹,曾經嘗試過,後來用得太少,最終放棄了。也知道有一些元件可以實現操作HMTL,比如mshtml,比如WebBrowser,然而總是感覺不太好,不太專業。猶猶疑疑,一直沒有開始,直到發現HtmlAgilityPack,如獲至寶,中間那個詞Agility,是敏捷、靈活的意思。

  以下文字,部分摘錄於周公部落格,有所改編。

  這裡是W3School的詳細介紹

  XPath簡明介紹
   XPath 使用路徑表示式來選取 XML 文件中的節點或節點集。節點是通過沿著路徑 (path) 或者步 (steps) 來選取的。
   下面列出了最有用的路徑表示式:
   nodename:選取此節點的所有子節點。 
   /是從根節點選取。 
   //是從匹配選擇的當前節點開始選擇當前節點之下的節點,而不考慮它們的位置和深度。 
   .(一個點)是選取當前節點。 
   ..(兩個點)選取當前節點的父節點。
   例如有下面一段XML:

<?xml version="1.0" encoding="utf-8"?> 
 <Articles> 
 <Article> 
   <Title>在ASP.NET中使用Highcharts js圖表</title> 
   <Url>http://zhoufoxcn.blog.51cto.com/792419/537324</Url> 
   <CreateAt type="en">2011-04-07</price> 
 </Article> 
 <Article> 
   <Title lang="eng">Log4Net使用詳解(續)</title> 
   <Url>http://blog.csdn.net/zhoufoxcn/archive/2010/11/23/6029021.aspx</Url> 
   <CreateAt type="zh-cn">2010年11月23日</price> 
 </Article> 
 <Article> 
   <Title>J2ME開發的一般步驟</title> 
   <Url>http://blog.csdn.net/zhoufoxcn/archive/2011/06/12/6540223.aspx</Url> 
   <CreateAt type="zh-cn">2011年06月12日</price> 
 </Article> 
 <Article> 
   <Title lang="eng">PowerDesign高階應用</title> 
   <Url>http://zhoufoxcn.blog.51cto.com/792419/166415</Url> 
   <CreateAt type="zh-cn">2007-09-08</price> 
 </Article> 
 </Articles> 

 針對上面的XML檔案,我們列出了帶有謂語的一些路徑表示式,以及表示式的結果:
 /Articles/Article[1]:選取屬於Articles子元素的第一個Article元素。 即第一組<Article></Article>。注意,從1開始,不是從0開始。
 /Articles/Article[last()]:選取屬於Articles子元素的最後一個Article元素。 即最後一組<Article></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。

 

HtmlAgilityPack API簡明介紹
 在HtmlAgilityPack中常用到的類有HtmlDocument、HtmlNodeCollection、
HtmlNode和HtmlWeb等。
 其流程一般是先獲取HTML,這個可以通過HtmlDocument的Load()或LoadHtml()來載入靜態內容,或者也可以HtmlWeb的Get()或Load()方法來載入網路上的URL對應的HTML。
 得到了HtmlDocument的例項之後,就可以用HtmlDocument的DocumentNode屬性,這是整個HTML文件的根節點,它本身也是一個HtmlNode,然後就可以利用HtmlNode的SelectNodes()方法返回多個HtmlNode的集合物件HtmlNodeCollection,也可以利用HtmlNode的SelectSingleNode()方法返回單個HtmlNode。

HtmlAgilityPack實戰

private void ParsePage()
        {
            docText = GetWebClient("http://www.bjjs.gov.cn/eportal/ui?pageId=308888");
            //string text = File.ReadAllText(@"C:\Users\sabre\Downloads\北京市專業人員資訊查詢.htm");
            //Post("http://www.bjjs.gov.cn/eportal/ui?pageId=308888", text,Encoding.UTF8.WebName);

            //PostWebRequest("http://www.bjjs.gov.cn/eportal/ui?pageId=308888", text, Encoding.UTF8);

            Post("http://www.bjjs.gov.cn/eportal/ui?pageId=308888", "currentPage=10", Encoding.UTF8.WebName);

            var doc = new HtmlDocument();
            doc.LoadHtml(docText);


            //人員資料
            var persons = doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[4]/div[3]/div[1]/div[2]/div[2]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]");
            HtmlNodeCollection personList = persons.SelectNodes(@"tr");
            foreach (var item in personList)
            {
                //根據class判斷,標題行跳過
                if (item.Attributes["class"] != null)
                {
                    string styleclass = item.Attributes["class"].Value;
                    if (styleclass == "DataGrid-Header") continue;//標題行跳過
                };

                var tds = item.SelectNodes(@"td");

                foreach (var td in tds)
                {
                    var a = td.InnerText;//獲取目標資料,如姓名、證件號等,此處可以開始進行相應的資料處理,如持久化
                }
            }


            //按鈕資料
            var buttons = doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[4]/div[3]/div[1]/div[2]/div[2]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]/tr[19]/td[1]/div[1]");

            var buttonList = buttons.SelectNodes(@"table//a");

            foreach (var item in buttonList)
            {
                string a = string.Empty;
                string b = string.Empty;
                if (item.Attributes["onclick"] != null)
                {
                    a = item.Attributes["onclick"].Value;
                    //var pattern = "/=(.+?);/g";
                    //var pattern = "/=(.*?);/";
                    var pattern = @"\d";
                    Regex reg = new Regex(pattern);
                    MatchCollection mc = reg.Matches(a);
                    foreach (Match m in mc)
                    {
                        b += m.Value;
                    }
                }
            }
        }

 

相關文章