Recent in Recipes

顯示具有 Xml 標籤的文章。 顯示所有文章
顯示具有 Xml 標籤的文章。 顯示所有文章

[奇怪的案例怪談2]使用C#抓取股市資料(內涵VB寫法)

趁現在過年時間比較沒事的時候整理一下筆記,因為最近開始在研究Python的關係,爬資料的東西又開始出現啦!記得之前有寫過Java使用Jsoup套件來抓取資料[文章在這],當時是因為寫java寫到一個愛不釋手境界,那這次是用C#的方式來爬資料,原因是因為...工作再碰C#跟VB...(哀...嘆氣,所以就廢話不多說我就來開始進入正題囉!
我們要使用HtmlAgilityPack這個程式庫來完成這次抓取資料的功能,其實各位可以上他的官網來瞭解: https://html-agility-pack.net/,其實網站有似乎更新所以提供許多範例可以參考,但載點好像沒有單獨dll檔提供下載有點可惜QQ,而要使用這套件有兩種方式來Import:
  1. 載入外部dll檔(提供檔案載點:
https://www.dllme.com/dll/files/htmlagilitypack_dll.html )
  1. 使用NuGet套件管理員Import(適用Visual Studio 2013之後版本)
這次的環境是2013版本,所以我用的是第1種方式(咦!?怎麼不是第二種XD),因為我有前人所留下的dll檔所以我就直接Import啦!這裡先解說如何加入dll檔,首先把注意力移到右邊專案中尋找參考後點擊右鍵有個加入參考如圖一所示,點擊後會顯示圖二的畫面,將dll檔案瀏覽選取後即可加入。

P3
※圖一 加入參考

P4
※圖二 加入本機端的dll檔

 而第二種方法是給2013版本後的做參考,這方法其實更方便,那就是直接去套件管理員尋找並安裝如圖三與圖四,這樣可以省去搜尋dll檔的時間!
P1
※圖三 開啟套件管理員

P2
※圖四 搜尋HtmlAgilityPack 套件並安裝

倘若把套件Import好後,就可以開始打程式囉!關於程式的部分也可以參考:
  1. https://dotblogs.com.tw/jackbgova/2014/06/10/145471
  2. https://dotblogs.com.tw/shadow/2011/12/12/61598
第一篇是我在整理文章內容找到的,老實說有點可惜,因為我的問題都解決才找到的,第二篇是我覺得可以先看的,我覺得觀念的東西可以先看這個,然後看第一篇,因為兩個寫法不一樣可以學習到很多。
首先先貼上畫面與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">&nbsp;&nbsp;
        台積電(
    <asp:Label ID="Label01" runat="server"></asp:Label>) &nbsp;&nbsp;&nbsp;
        時間 :
        <asp:Label ID="Label03" runat="server"></asp:Label>&nbsp; &nbsp;&nbsp; </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>&nbsp;&nbsp; &nbsp;
        最低價 :<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>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
        <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>

 
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(); } } } }
最後呈現的結如下圖五,我選了台積電當這次的範例,哇!!!!是紅字喲!如果有人買他們的股票我會含淚幫你們鼓掌的!(因為我買不起阿~~)...... 我稍微解說一下我程式的寫法,我寫一個方法然後透過Page_Load來呼叫他,然後用例外框架來顯示如果發生錯誤是如何發生的,可能有些人不習慣用例外直接寫也是可以的,不過還是要養成良好的習慣比較好喲!再來是解析標籤,他的路徑表示是XPath,大家可以參考微軟官網的資料 https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx ,然後解析出來的資料用Split('')來存到陣列裡,其實也可以直接解析轉字串,但陣列真的方便許多!

P5
※圖五 顯示結果

 那如果是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的朋友也不用擔心,前面的套件也是一樣的做法,請大家安心服用!

最後要跟各位分享這次的奇怪案例,因為我們公司爬資料這塊是用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的程式碼就知道標籤的例外處裡就是空白,其實美觀以外就是跟我說這功能有問題了,也不會馬上被其他單位的主管發現(噓!~~~~)
好的!以上是我這次分享的奇怪案例

簡單使用JSoup抓取xml網站資料(含android範例)

  關於抓資料,第一個想法應該是python,不過因為學了java,想透過java抓取資料,於是開啟我查詢java爬資料的資料,除了查到ETL外,jsoup應該是最方便的"套件"了。
  jsoup說明白他就是java的library(jar),也就是人家寫好的套件然後造福我們這些寫程式的人用,這真的是很厲害的地方!我在網路上參考了
【JAVA】使用JSOUP讓JAVA取得網頁上的文字 (Example for download)這篇文章雖然寫得很簡單但是語法跟解說很清楚謝謝大大 =D,那我的廢話到這裡開始解說我的程式囉!

  jsoup方便的原因是因為不管是html,還是xml都是屬於標籤語言,而jsoup就是根據標籤抓取裡面的資訊,在打程式之前我們有些前置作業要做:
1.前往官方網站 http://jsoup.org/download 下載jar
2.開啟java專案匯入jar(這裡使用netbean)
12
其實netbean和eclipse匯入的方法差不多所以大家不用太擔心。
本篇文章是抓取政府opendata的資料放置本機伺服器,其資料格式為xml,
下載網址如下:
程式碼:CWBTSU.xml

<br data-mce-bogus="1">

CWB-TSU10300101-2014-0920-073000

CWB_SC@scman.cwb.gov.tw

2014-09-20T07:30:00+08:00

Actual

Issue

CWB

Public

海嘯資訊

zh-TW

2014-09-20T07:30:00+08:00

2014-09-20T15:30:00+08:00

海嘯警報

103001

第1報

頃獲太平洋海嘯警報中心通知,103年09月20日07時21分(臺灣時間),在震央東經137﹒41度、北緯9﹒19度,發生規模8﹒7地震。該中心研判可能引發海嘯,經初步推算,海嘯湧浪預計臺灣時間103年09月20日09時41分到達臺灣,請臺灣沿海地區提高警覺嚴加防範。

橙色

http://scweb.cwb.gov.tw/GraphicContent.aspx?ItemId=3&amp;fileString=10300101

2014-09-20T07:21:00+08:00

137.41

9.19

亞普海溝

10.0

8.7

美國太平洋海嘯警報中心

東南沿海地區

臺東縣成功鎮至屏東縣滿州鄉沿岸

1至3公尺

2014-09-20T09:41:00+08:00

橙色

predict

東部沿海地區

宜蘭縣南澳鄉至臺東縣長濱鄉沿岸

1至3公尺

2014-09-20T09:51:00+08:00

橙色

predict

東北沿海地區

宜蘭縣頭城鎮至蘇澳鎮沿岸

1至3公尺

2014-09-20T09:51:00+08:00

橙色

predict

西南沿海地區

臺南市至屏東縣恆春鎮沿岸

1至3公尺

2014-09-20T10:01:00+08:00

橙色

predict

北部沿海地區

新北市及基隆市沿岸

1至3公尺

2014-09-20T10:11:00+08:00

橙色

predict

海峽沿海地區

桃園縣至嘉義縣沿岸,以及澎湖縣、金門縣與連江縣等離島區域

小於1公尺

2014-09-20T10:51:00+08:00

黃色

predict



其實這裡都已經定義好資料了,每個標籤都有他們所呈現的資訊,所以在抓取資料時會相對的單純很多!
接下來直接來看程式碼

<Java程式端>
public static void main(String[] args) throws Exception {

URL url = new URL("server位置+檔名,以本機為例EX:http://127.0.0.1/ CWBTSU.xml ");

Document xmlc =  Jsoup.parse(url, 3000); //使用Jsoup解析網頁

try {

Elements datas = xmlc.select("warningArea"); //解析的元素為warningArea的標籤

for(Element data : datas){

System.out.println(data.text());

}

//使用for each取出資料並顯示

System.out.println("Is "+datas.get(0).text());

//這裡是取出warningArea標籤的第一個內容,即get(0),如果要取第二個內容為get(1)

} catch (Exception e) {

}
}


其執行結果為
3
表示我們已經把warningArea標籤內容取出了,大家可以參考一下。
如果程順利的顯示結果就表示完成了!那如果要搭配android的話如果java程式框架已經出來了,不過要使用執行緒來執行,因為有牽涉網路存取,可能會導致程式timeout,接下來附上程式碼。
首先是畫面的layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.jsouptest.MainActivity">

<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World!"
android:id="@+id/hi"
android:layout_above="@+id/button"
android:textAllCaps="false"
android:textAlignment="center" />

<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:id="@+id/button" />
</RelativeLayout>
再來是權限檔我們需要網路存取權限
 ※<uses-permission android:name="android.permission.INTERNET" /></pre>
所以所有程式碼如下
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.jsouptest">
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
最後就是java檔了
 

 import android.app.Activity;
 import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;

 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;

 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;

public class MainActivity extends Activity {
  TextView hi;
  URL url;
  Thread go;
  StringBuilder tmp = new StringBuilder();
  int i = 1;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     Button button = (Button) findViewById(R.id.button);
     hi = (TextView) findViewById(R.id.hi);

     button.setOnClickListener(new Button.OnClickListener() {
       @Override
       public void onClick(View v) {
         go = new Thread(readData);
         go.start();
       }
     });

 }

 private Runnable readData = new Runnable() {
   public void run() {
   try {
   //(要解析的文件,timeout)

      url = new URL("在這裡輸入server位置與檔案(這裡檔案名為上面的xml) ");

      Document xmlDoc =&nbsp; Jsoup.<em>parse</em>(url,3000);
      Elements title = xmlDoc.select("warningArea"); <em>//要解析的tag元素為warningArea這標籤</em>

      //Elements tables = xmlDoc.select("center&gt;table");
      tmp.delete(0,tmp.length());
      for(int i =0; i&lt;title.size() ; i++){
        Log.<em>e</em>("Kevin Test",title.text());
        tmp.append(i+1+title.get(i).text()+"&nbsp;&nbsp;&nbsp; "+"\n\n");

      }
    hi.setText(tmp);
    //System.out.println("Is "+title.get(0).text());
    } catch (Exception e) {
  }
 }
 };
 }



其顯示結果如下:
messageImage_1485014148608.jpg
這是一開始的畫面
messageImage_1485014154111.jpg
當按下按鈕時,會把xml中所有標籤資訊解譯出來
  jsoup是一個很好用的套件,其實他可以運用的功能很多,這裡只是冰山一角而已,至於想要如何呈現或是在資料中得知有用的資訊又是一項艱難的任務了。
  所以這是為什麼資料分析會那麼火紅的原因之一,小弟不是做資料分析的,但是在爬資料的時候都有先預設說爬什麼資料與想知道什麼資訊,從爬到的資料中尋找出我想知道的答案,所以大家也可以想想,自己手上的資料是否也有貨真價實的資訊等著我們發覺、探究和解惑。