近期,有一個需求,需要解析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; } } } }