我們要使用HtmlAgilityPack這個程式庫來完成這次抓取資料的功能,其實各位可以上他的官網來瞭解: https://html-agility-pack.net/,其實網站有似乎更新所以提供許多範例可以參考,但載點好像沒有單獨dll檔提供下載有點可惜QQ,而要使用這套件有兩種方式來Import:
- 載入外部dll檔(提供檔案載點:
- 使用NuGet套件管理員Import(適用Visual Studio 2013之後版本)

※圖一 加入參考

※圖二 加入本機端的dll檔

※圖三 開啟套件管理員

※圖四 搜尋HtmlAgilityPack 套件並安裝
第一篇是我在整理文章內容找到的,老實說有點可惜,因為我的問題都解決才找到的,第二篇是我覺得可以先看的,我覺得觀念的東西可以先看這個,然後看第一篇,因為兩個寫法不一樣可以學習到很多。
首先先貼上畫面與C#的程式碼
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="CshapClimbDemo.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<style type="text/css">
#stock {
text-align: left;
}
</style>
</head>
<body style="text-align: center">
<form id="form1" runat="server">
<div id="stock">
<span id="title-stock">
台積電(
<asp:Label ID="Label01" runat="server"></asp:Label>)
時間 :
<asp:Label ID="Label03" runat="server"></asp:Label> </span><br />
<span style ="padding-left :20px;">成交價 :<asp:Label ID="Label07" runat="server" Font-Bold="True" Font-Size="15pt"></asp:Label></span> <br />
<span style ="padding-left :20px;">成交量 :<asp:Label ID="Label08" runat="server"></asp:Label></span><br />
<span style ="padding-left :20px;">最高價 :<asp:Label ID="Label05" runat="server"></asp:Label>
最低價 :<asp:Label ID="Label06" runat="server"></asp:Label></span> <br />
<span style ="padding-left :20px; text-align: left;">開盤價 :<asp:Label ID="Label04" runat="server"></asp:Label>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="WebForm1.aspx" Target="_self">Refresh</asp:HyperLink>
<br />
<br />
<asp:Label ID="Label9" runat="server" Text="Label"></asp:Label>
</span></div>
</form>
</body>
</html>
最後呈現的結如下圖五,我選了台積電當這次的範例,哇!!!!是紅字喲!如果有人買他們的股票我會含淚幫你們鼓掌的!(因為我買不起阿~~)...... 我稍微解說一下我程式的寫法,我寫一個方法然後透過Page_Load來呼叫他,然後用例外框架來顯示如果發生錯誤是如何發生的,可能有些人不習慣用例外直接寫也是可以的,不過還是要養成良好的習慣比較好喲!再來是解析標籤,他的路徑表示是XPath,大家可以參考微軟官網的資料 https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx ,然後解析出來的資料用Split('')來存到陣列裡,其實也可以直接解析轉字串,但陣列真的方便許多!using HtmlAgilityPack; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace CshapClimbDemo { public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Getstock(); } private void Getstock() { //int i = 0; try { //1.指定要開啟的網站,然後使用MemoryStream暫存 此範例為奇摩台積電股票 WebClient climb = new WebClient(); MemoryStream ms = new MemoryStream(climb.DownloadData("http://tw.stock.yahoo.com/q/q?s=2330")); //2.使用HtmlDocument載入第一層網頁資料 HtmlDocument doc = new HtmlDocument(); doc.Load(ms, Encoding.Default); HtmlDocument docStockContext = new HtmlDocument(); docStockContext.LoadHtml(doc.DocumentNode.SelectSingleNode("html[1]/body[1]/center[1]/table[2]/tr[1]/td[1]/table[1]").InnerHtml); //3.根據資料在哪個標籤就讀取標籤的內容 我這裡使用陣列儲存讀到的資料 HtmlNodeCollection nodeHeaders = docStockContext.DocumentNode.SelectNodes("./tr[1]/th"); String[] sValues = docStockContext.DocumentNode.SelectSingleNode("./tr[2]").InnerText.Trim().Split(' '); //※可以使用這個迴圈觀察資料的內容以及分割情況 /*foreach(String s in sValues){ this.Label9.Text += s +" "+i++ +" "; }*/ //4.最後顯示資料 可以使用我存取的陣列來呈現 或是把剛剛讀取標籤的內容轉成字串來呈現 this.Label01.Text = docStockContext.DocumentNode.SelectSingleNode("./tr[2]/td[1]").InnerText.Trim().ToString().Substring(0,4); this.Label03.Text = sValues[16].Trim(); this.Label04.Text = sValues[128].Trim(); this.Label05.Text = sValues[144].Trim(); this.Label06.Text = sValues[160].Trim(); this.Label07.Text = sValues[32].Trim() + " ( " + sValues[80].Trim() + " )"; this.Label08.Text = sValues[96].Trim(); //※顏色編輯如果漲停就紅色 跌就綠色 if (sValues[80].Trim().IndexOf('△') > -1) { Label07.ForeColor = System.Drawing.Color.Red; } else { Label07.ForeColor = System.Drawing.Color.Green; } } catch (Exception e) { this.Label9.Text = e.Message.ToString(); } } } }

※圖五 顯示結果
那如果是VB怎麼打呢?我就不多說直接貼給大家程式碼
如果各位有感覺的話可以發現,其實兩個程式碼是差不多的(邏輯上)所以如果是寫VB的朋友也不用擔心,前面的套件也是一樣的做法,請大家安心服用!Imports System.IO Imports System.Net Imports HtmlAgilityPack Imports System.Text Partial Class Stock2330 Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load GetData2330() End Sub Private Sub GetData2330 () Try Dim client As WebClient = New WebClient() Dim ms As MemoryStream = New MemoryStream(client.DownloadData("http://tw.stock.yahoo.com/q/q?s=2330")) ' Dim doc As HtmlDocument = New HtmlDocument() doc.Load(ms, Encoding.Default) Dim docStockContext As HtmlDocument = New HtmlDocument() '因錯誤訊息顯示無法讀取網站資料 XPath路徑做更改 docStockContext.LoadHtml(doc.DocumentNode.SelectSingleNode("html[1]//body[1]//center[1]//table[2]/tr[1]/td[1]/table[1]").InnerHtml) ' Dim nodeHeaders As HtmlNodeCollection = docStockContext.DocumentNode.SelectNodes("./tr[1]/th") Dim sValues() As String = docStockContext.DocumentNode.SelectSingleNode("./tr[2]").InnerText.Trim().Split(" ") ''輸出資料() 'Dim i As Integer = 0 'For Each nodeHeader As HtmlNode In nodeHeaders ' txtMsg.Text = txtMsg.Text + nodeHeader.InnerText + ":" + nodeData(i).InnerText ' i += 1 'Next Me.Label01.Text = sValues(0).Trim.Substring(0, 4) Me.Label03.Text = sValues(16).Trim Me.Label04.Text = sValues(128).Trim Me.Label05.Text = sValues(144).Trim Me.Label06.Text = sValues(160).Trim Me.Label07.Text = sValues(32).Trim + " ( " + sValues(80).Trim + " ) " Me.Label08.Text = sValues(96).Trim If sValues(80).Trim.IndexOf("△") > -1 Then Me.Label07.ForeColor = Drawing.Color.Red ElseIf sValues(80).Trim.IndexOf("▽") > -1 Then Me.Label07.ForeColor = Drawing.Color.Green End If Catch ex As Exception Me.Label01.Text = " " Me.Label03.Text = " " Me.Label04.Text = " " Me.Label05.Text = " " Me.Label06.Text = " " Me.Label07.Text = " " Me.Label08.Text = " " End Try End Sub
最後要跟各位分享這次的奇怪案例,因為我們公司爬資料這塊是用VB來爬的,其實這是前人寫好的功能,我也沒有理他,好死不死去年12月底的時候這個功能給我掛掉了,然後內網網站呈現紅通通的錯誤訊息讓我差點嚇到100天才能回去過年!!於是我看來看去發現他的程式首先沒有用例外框架來包覆,所以我補上例外框架讓可以用的功能能正常運作,在來就是看為什麼會突然失效,我查了一下判定是沒讀到股市資料,這就表示XPath的抓取位置是有問題的,於是我就看了半天再"html[1]/body[1]/center[1]/table[2]/tr[1]/td[1]/table[1]"這個路徑補了一些東西那就是
"html[1]//body[1]//center[1]//table[2]/tr[1]/td[1]/table[1]" 大家有看出來嗎?我在table之前都多加了一個斜線,兩條斜線表示抓取所有那個標籤的資料,我就想一條抓不到我就設兩條就跑不了了吧!結果一更新上去後...沒錯...,他好了...所以為什麼會突然失效呢?我也是不知道,不過還好解決了這個問題,另外我在例外處裡的地方如果這功能有錯誤就顯示無資料,其實眼尖的朋友可以看一下我VB的程式碼就知道標籤的例外處裡就是空白,其實美觀以外就是跟我說這功能有問題了,也不會馬上被其他單位的主管發現(噓!~~~~)
好的!以上是我這次分享的奇怪案例