最近要來當網路爬蟲,以前常用的VBA套件是Selenium,這個套件也有出.Net和Python版本,主要是使用Xpath技術去指定要抓取的網頁資料。

雖然方便,但是有一個很大的缺點是必須開啟瀏覽器去運行,也就是說要掛載在不同的瀏覽器,就要去他們網站下載相對應的驅動器。

當然,如果瀏覽器更新,他們也會做出相對應的新版本,只是有些麻煩,加上要做到背景運行,還要再多裝一個新的瀏覽器。

 

後來找到C#的專用套件,也就是HtmlAgilityPack,可以參考十一的.net 部落的網站有一篇很不錯的講解[轉] HTML Agility Pack:簡單好用的快速 HTML Parser

這是Html Agility Pack的官網,這是官網的使用說明文件

Html Agility Pack可以解析鬆散的html結構,而我在試寫的時候因為已經習慣xpath語法,因此在稍後的demo中也會使用xpath語法來呈現。

簡單的xpath教學可以參考W3School(應該是仿W3C的大陸網站啦XD)

從十一的.net 部落可以得知此套件是法國人寫的專案,所以在擷取資料時遇到中文會變成亂碼

因此需要在剛開始載入整份Html時就要透過 MemoryStream 使用 HtmlDocument.Load() 方法,

才可以指定中文的編碼,而我試寫了之後認為使用Encoding.UTF8就可以解析中文了。

接下來,我要去抓取期交所的網頁http://www.taifex.com.tw/cht/5/stockMargining

1. 開新專案,使用NuGet取得HtmlAgilityPack.dll

 

2. using會用到的Package

using System.Data;     //使用System.Data.DataTable
using System.IO;       //使用System.IO.MemoryStream
using System.Net;      //使用System.Net.WebClient
using HtmlAgilityPack;

3. 宣告全域變數

        private WebClient webClient = new WebClient();
        private MemoryStream memoryStream = null;

        public string UpdateDate = "";
        public DataTable dt = new DataTable();

4. 建立一個方法,開始編寫這次的Demo主體,首先使用MemoryStream載入WebClient抓取的網頁資料,

並建立第一個HtmlDocument取得整個網頁的轉碼後資料。

用MemoryStream的原因在上面已經說了,需要讓HtmlDocument有辦法轉碼才能解析中文。

 

            //HTML Agility Pack預設編碼應是法文編碼,所以如果是讀取中文 HTML 內容的話,
            //無法直接使用HtmlDocument.LoadHtml() 方法,而要透過MemoryStream使用HtmlDocument.Load()方法,才可以指定中文的編碼。
            memoryStream = new MemoryStream(webClient.DownloadData(@"http://www.taifex.com.tw/cht/5/stockMargining"));

            //使用HtmlDocument.Load()進行編碼,使用UTF8編譯,取得整份網頁結構
            HtmlDocument doc = new HtmlDocument();
            doc.Load(memoryStream, Encoding.UTF8);

5. 我要取得表格資料以及更新日期,所以從doc往下再取得網頁主要資料,

取得頁面主要資料的結構後,分別再向下搜尋取得更新日期,存入UpdateDate,

再創一個新的HtmlDocument儲存向下搜尋到的表格結構

            //從doc向下取得目標資料的html結構
            HtmlDocument docData = new HtmlDocument();
            docData.LoadHtml(doc.DocumentNode.SelectSingleNode(@"//div[@name='printhere']")
                                             .InnerHtml);

            //獲得更新日期
            UpdateDate = docData.DocumentNode.SelectSingleNode(@"/div/p/span").InnerText;

            //從docData向下取得網頁上目標表格的html結構
            HtmlDocument dt_html = new HtmlDocument();
            dt_html.LoadHtml(docData.DocumentNode.SelectSingleNode(@"//table[@class='table_c']")
                                                 .InnerHtml);

6. 將dt_html的結構資料存入dt,使用的技巧詳見https://html-agility-pack.net/zh-CN/knowledge-base/18090626/

            //批次取得th資料,利用這些資料進行IEnumarable創造dt的Column
            HtmlNodeCollection headers = dt_html.DocumentNode.SelectNodes(@"//tbody/tr/th");

            foreach (HtmlNode header in headers)
            {
                dt.Columns.Add(header.InnerText);
            }

            //可用rows取得所有列的資料,也可直接寫在foreach裡面,tr[td]的意思是選取「所有tr之下有td」的tr們
            //HtmlNodeCollection rows = dt_html.DocumentNode.SelectNodes(@"//tr[td]");
            foreach (HtmlNode row in dt_html.DocumentNode.SelectNodes(@"//tr[td]"))
            {
                //再用SelectNodes批次取得所有td的資料,利用lambda語法取得所有InnerText
                dt.Rows.Add(row.SelectNodes(@"td").Select(td => td.InnerText.Trim()).ToArray());
            }

7. 寫回UI更新

            lbUpdateDate.Text = updateDate;
            dataGridView1.DataSource = new BindingSource(dt, null);
            dataGridView1.ColumnHeadersDefaultCellStyle.WrapMode = DataGridViewTriState.False;
            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;

 

後記:要完成自己想要的交易系統,實在是工程浩大,很多東西都要自己籌備,但千里之行,始於足下,努力不懈,方能步行天下。

arrow
arrow
    文章標籤
    C# HtmlAgilityPack
    全站熱搜

    wings890109 發表在 痞客邦 留言(0) 人氣()