Recent in Recipes

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

[奇怪的案例怪談1]如何使用PHP寄送郵件

哈囉!我又出現了!非常非常久沒有打文章了,因為這兩年其實小弟我有一些原因所以比較沒有時間碰,現在可以好好的來介紹一些功能與案例,之後也會介紹一些容易上手的東西,希望大家對於資訊,尤其是程式設計更有興趣。

今天要介紹的是如何使用PHP來寄送郵件,這個功能我覺得對於網站設計應該是很實用的,近期正好有架設網站的需求,以及維護的案例,有幸來跟大家介紹一下,我們使用的套件為PHPMailer,主要架構為PHP撰寫,其實也爬了許多網站這裡來做個統整,首先來先下載PHPMailer套件,提供Github的網址https://github.com/Synchro/PHPMailer
接下來要做的就是些設定,首先打開要設定轉寄寄信的Gmail然後進去後到右上角的齒輪如下圖,然後點下去之後選擇設定

1
※Gmail設定

之後會顯示一些選項,選擇轉寄和POP/IMAP,然後將POP功能與IMAP功能打開如下圖所示

2
※打開POP/IMAP功能

關於POP/IMAP與SMTP呢…還是稍微稍微的介紹一下,先附上資料連結
  1. https://support.office.com/zh-hk/article/imap-%E5%92%8C-pop-%E6%98%AF%E4%BB%80%E9%BA%BC%EF%BC%9F-ca2c5799-49f9-4079-aefe-ddca85d5b1c9
  2. https://blog.xuite.net/cyclone/blog/56713570-%E4%B8%80%E6%AC%A1%E6%90%9E%E6%87%82SMTP%E3%80%81POP3%E3%80%81IMAP%E6%98%AF%E4%BB%80%E9%BA%BC
P.S.因為我也是查了資料才知道XD
SMTP是規範在RFC 821,其Port為25,當初是給公司內部郵件傳遞,隨著時間的演進現今的規範比較完整統一了電子郵件的規格。
POP與IMAP的不同為POP為一次下載郵件後刪除,也就是說只有一台能夠讀取郵件資料,IMAP則是在線上讀取資料,不做下載的動作,如果根據現今的要求來看,IMAP是比較符合使用者需求的,因為使用者不可能永遠只用一種裝置看郵件。
PS.其實我有事過沒有設定也可以寄送XD 但是我覺得搞不好是之前有設定然後網頁瀏覽器有記錄到,所以還是乖乖設定比較保險
好的!其實我扯遠了,Gmail的部分還有一個東西要設定,開啟安全性設定開啟允許較低安全性的應用程式將他打開,如下圖所示

3
※允許較低安全性的應用程式

關於Gmail已經設定完成了,還有一個東西要設定(還來啊!=口=’’’) ,既然是PHP當然少不了Web server囉,因為小弟是使用XAMPP來設定,所以我這裡教的是XAMPP的修改,如果是使用AppServ可以參考這篇非常詳細的文章,這篇也解決了我許多問題
http://edu.unethost.com/%E4%B8%BB%E6%A9%9F%E7%9B%B8%E9%97%9C/phpmailer-%E9%81%8B%E7%94%A8gmail%E4%BE%86%E5%AF%84%E4%BF%A11/
打開XAMPP,在Apache中點選PHP.ini 如下圖

4
※XAMPP Apache php.ini

終於我們把需要設定的都設定完了!再來是將套件與程式碼放置Web server中(htdocs資料夾裡),程式碼雖然Github內文中有解說如何使用,不過還是要提供一下程式碼啦!

 
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'PHPMailer-master/src/Exception.php';
require 'PHPMailer-master/src/PHPMailer.php';
require 'PHPMailer-master/src/SMTP.php';
$content="<p style='color:#F00'>訪客: "."test".", 於官方網站內發出一則詢問信件, 請您務必盡快與他(她)聯絡或回覆詢問!! </p>姓名:"."test"."<br />"."電話:"."0955555555"."<br />"."E-MAIL:"."test@test.org"."<br />"."公司:"."test"."<br />"."<br />"." "."意見或詢問:"."test"."<br/><br/><h4 style='color:#F00'>< 本信件為系統寄發,請勿直接回覆 ></h4>";
$mail= new PHPMailer(); //建立新物件
$mail->IsSMTP(); //使用SMTP方式寄信
$mail->SMTPAuth = true; //設定SMTP需要驗證
$mail->SMTPSecure = "ssl"; // Gmail的SMTP主機需要使用SSL連線
$mail->Host = "smtp.gmail.com"; //Gamil的SMTP主機
$mail->Port = 465; //SMTP預設為25 Gamil的SMTP主機的埠號。
$mail->CharSet = "utf-8"; //郵件編碼
$mail->Username = "XXXX@gmail.com"; //Gamil帳號
$mail->Password = "XXXX"; //Gmail密碼
$mail->From = "epaper@test.com"; //寄件者信箱
$mail->FromName = "TEST Technology Corp."; //寄件者姓名
$mail->Subject ="這是SMTP測試信"; //郵件標題
$mail->Body =$content; //郵件內容
$mail->IsHTML(true); //郵件內容為html
$mail->AddAddress("OOO@gmail.com"); //收件者郵件及名稱
$mail->AddBCC(" "); //設定 密件副本收件者
if(!$mail->Send()){
echo "Fail: " . $mail->ErrorInfo;
}else{
echo "<b>您好!已收到您的留言,會盡快回覆</b>";
}
?>
如果執行以後顯示如下圖就表示成功寄出信件囉!
6
※郵件成功寄出(上為網頁顯示  下為Gmail的執行情況)


 [奇怪的案例怪談1]
最後分享一段奇怪的案例,小弟公司的網站也是用這個功能寄送郵件,而且是2013年的時候就有了(小弟那時候還沒來哈哈哈哈),往後基本上那台server就沒有去動了 (寄送信件php檔最後修改日期是2013年…)。

而故事就發生在2018年11月7號的時候,有其他單位的同人跟我反應說郵件功能有問題,因為我們網站有做一個功能叫”聯絡我們”,這個功能是負責收客戶的信,而我們公司會接許多國外客戶的案子所以這個功能無形中扮演了重要的腳色。

結果我去看錯誤訊息,顯示為SMTP Error: Could not authenticate.,這個訊息我爬了許多文跟改了一堆程式 (就是拿掉後再放回去的循環),搞了我一天的時間,Could not authenticate 我就是要寄信阿,信件為什麼驗證不過呢?它就是信嗎?驗證什麼?...咦!?驗證信件?,於是我隔天把”$mail->SMTPAuth = true; ”這行註解以後…就好了!...見鬼啦!!怎麼回事啊!!怎麼原本沒事的功能突然就掛了!?...算了…已經解決了這個問題可喜可賀!

其實在工作或是研究上都會遇到突然凸槌的問題,自己研究的問題發生影響比較小,因為所有東西是一手掌握,但是工作時候就不一樣,你面對的是前人或是現在的同事一起合作的程式,有時候需要了解對方的邏輯就要一段時間了,以後有機會在分享一些奇怪的案例,那就跟各位介紹到這裡囉!