最近要來當網路爬蟲,以前常用的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;
後記:要完成自己想要的交易系統,實在是工程浩大,很多東西都要自己籌備,但千里之行,始於足下,努力不懈,方能步行天下。
留言列表