ตอนที่ผมเขียน Blog น่าจะเป็นช่วงปลายปี 2016 นะครับ พอดีวันนี้เหลือเวลาอีก 2-3 ชั่วโมงจะเลิกงาน ผมเลยลองทำ Project ง่ายๆ ดูครับ โดยดึงข้อมูลจากเว็บ เกี่ยวกับข้อมูลพื้นฐานของหุ้นนะครับ ก่อนที่จะทำ สิ่งที่ตต้องทำลอง Research Library ดูก่อนครับ ว่ามีอะไรช่วยได้ไหม เท่าที่ดูมี Library ที่เหมาะสม ดังนี้ครับ
- HtmlAgilityPack สามารถ Download มาจาก Nuget ได้เลยครับ
ถึงเวลาเริ่มทำ
- ต้องไปส่องเว็บก่อน ว่าเราดึงข้อมูลอะไร
- ลองดู Code ของ HTML ที่ได้ ลอง View Source ครับ
- สิ่งที่ผมสนใจ คือ ข้อมูลงบการเงิน/ผลประกอบการ ตามนี้ครับ
<table class="table table-hover table-info"> <caption> <span style="float: right;">(หน่วย: ล้านบาท)</span> </caption> <thead> <tr align="center" valign="middle"> <th height="30"><strong>งวดงบการเงิน<br> ณ วันที่</strong></th> <th ><strong>ไตรมาส3/59<br>30/09/2559</strong></th> </tr> </thead> <tbody> <tr > <td height="15" style="text-align:left;" colspan="1"><strong>บัญชีทางการเงินที่สำคัญ</strong></td> <td style="background-color: #EAF0FE;"> </td> </tr> <tr > <td height="15" style="text-align:left;" valign="middle">สินทรัพย์รวม</td> <td style="background-color: #EAF0FE;" >80,682.71 </td> </tr> <tr > <td height="15" style="text-align:left;">หนี้สินรวม</td> <td style="background-color: #EAF0FE;" >51,430.42 </td> </tr> <tr > <td height="15" style="text-align:left;">ส่วนของผู้ถือหุ้น</td> <td style="background-color: #EAF0FE;" >29,160.30 </td> </tr> <tr > <td height="15" style="text-align:left;">มูลค่าหุ้นที่เรียกชำระแล้ว</td> <td style="background-color: #EAF0FE;" >15,285.00 </td> </tr> <tr > <td style="text-align:left;" height="15">รายได้รวม</td> <td style="background-color: #EAF0FE;" >9,775.47 </td> </tr> <tr > <td style="text-align:left;" height="15">กำไรสุทธิ</td> <td style="background-color: #EAF0FE;" >2,014.22 </td> </tr> <tr > <td style="text-align:left;" height="15">กำไรต่อหุ้น (บาท)</td> <td style="background-color: #EAF0FE;" >0.13 </td> </tr> <tr > <td height="15" style="text-align:left;" colspan="1"><strong>อัตราส่วนทางการเงินที่สำคัญ</strong></td> <td style="background-color: #EAF0FE;"> </td> </tr> <tr > <td style="text-align:left;" height="15">ROA(%)</td> <td style="background-color: #EAF0FE;" >7.21 </td> </tr> <tr > <td style="text-align:left;" height="15">ROE(%)</td> <td style="background-color: #EAF0FE;" >10.32 </td> </tr> <tr > <td style="text-align:left;" height="15">อัตรากำไรสุทธิ(%)</td> <td style="background-color: #EAF0FE;" >20.60 </td> </tr> <thead> <tr align="center" valign="middle"> <th height="30"><strong>ค่าสถิติสำคัญ<br> ณ วันที่</strong></th> <th ><strong>29/12/2559</strong></div></th> </tr> </thead> <tr > <td style="text-align:left;" height="15">ราคาล่าสุด(บาท)</td> <td style="background-color: #EAF0FE;" >7.45 </td> </tr> <tr > <td style="text-align:left;" height="15">มูลค่าหลักทรัพย์ตามราคาตลาด</td> <td style="background-color: #EAF0FE;" >113,873.25 </td> </tr> <tr align="center" valign="middle"> <td class="topicbg" style="text-align:left;">วันที่ของงบการเงินที่ใช้คำนวณค่าสถิติ</td> <td style="background-color: #EAF0FE;" >30/09/2559 </td> </tr> <tr > <td style="text-align:left;" height="15">P/E (เท่า)</td> <td style="background-color: #EAF0FE;" >37.91 </td> </tr> <tr > <td style="text-align:left;" height="15">P/BV (เท่า)</td> <td style="background-color: #EAF0FE;" >3.91 </td> </tr> <tr > <td style="text-align:left;" height="15">มูลค่าหุ้นทางบัญชีต่อหุ้น (บาท)</td> <td style="background-color: #EAF0FE;" >1.91 </td> </tr> <tr > <td style="text-align:left;" height="15">อัตราส่วนเงินปันผลตอบแทน(%)</td> <td style="background-color: #EAF0FE;" >0.94 </td> </tr> </tbody> </table>
- หลังจากเข้าใจ Pattern แล้ว ลองลุยเขียน Code เลย สำหรับผมที่โง่ฝั่งเว็บมากๆ ขอเป็น Winform ครับ ลากๆแปะๆเสร็จ
ลองมาดูวิธีใช้ HtmlAgilityPack ดึงกว่า
- ก่อนใช้ HtmlAgilityPack เราต้องมีข้อมูลเว็บก่อนครับ สำหรับผมใช้ HttpWebRequest กับ HttpWebResponse เข้ามาช่วยครับ ลองดู Code ตัวอย่างได้เลย
private string GrabWebpageData(string url) { string appURL = url; HttpWebRequest Request = WebRequest.Create(appURL) as HttpWebRequest; HttpWebResponse Response = (HttpWebResponse)Request.GetResponse(); StreamReader srResponseReader = new StreamReader(Response.GetResponseStream(), Encoding.UTF8); string strResponseData = srResponseReader.ReadToEnd(); srResponseReader.Close(); return strResponseData; }
- เมื่อมีข้อมูลแล้ว ต่อมาเป็นหน้าที่ของ HtmlAgilityPack ผมต้องโยนข้อมูลจาก Setup แรกเข้าไป มันมี 2 แบบ
- Load - ส่ง Stream หรือ ส่ง file path เข้าไปก็ได้ครับ
- LoadHtml - โยน String เว็บเข้าไปเลยครับ (ผมใช้วิธีนี้แหละ ลองดูตัวอย่าง Code ด้านล่างเลย
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(webdata);
- เนื่องจากเว็บอาจจะใหญ่เกินไป เรามาใช้ลองข้อมูลในการค้นหาลงครับ อย่างผมกำหนด Criteria เป็น
string match = "<table class=\"table table-hover table-info\">";
- ลองสังเกตุ HTML ส่วนที่ผมสนใจในข้างต้นครับ จากนั้นก็ตัดมันออกมาครับ
- คราวนี้มาลองใช้ HtmlAgilityPack โดยผมสนใจเฉพาะ Tag "Table" นะครับ ผมใช้ตัว doc.DocumentNode.SelectNodes("//table") เพื่อให้ได้ตัว HtmlNode ครับ โดยตัว HtmlNode มองว่าเป็น Graph ครับ มันเก็บข้อมูล และมีความสัมพันธ์ไปยัง Node อื่นๆครับ
foreach (HtmlNode table in doc.DocumentNode.SelectNodes("//table")) { //Do some business here!!! }
ก่อนหน้านี้ foreach คราวนี้ลองดึงมาทั้งยวงครับ ตาม Code เลย สังเกตุดีนะครับ ผมดึงมาทั้ง Tag และเลย
HtmlNode[] tdData = trNode.DocumentNode.SelectNodes("//td|//th").ToArray();
- เนื่องจากมันเป็น C# มันใช้ Linq ได้ด้วยครับ อย่างของผมจะเอา Attribute ชื่อ height ที่มีขนาด 15 และ 30 ครับ โดย Method ที่ใช้ คือ GetAttributeValue เป็นของ HtmlNode ครับ
HtmlNode[] detail = content.DocumentNode.SelectNodes("//td").ToArray(); detail = detail.Where(x => x.GetAttributeValue("height", "0") == "15" || x.GetAttributeValue("height", "0") == "30").ToArray();
- Property ที่น่าสนใจ สำหรับที่ผมลองใช้นะครับ
- ChildNode - บอกว่ามี Node ลูกไหม เช่น <tr> มี <td> เป็น node ลูก
- InnerHtml - ข้อมูล HTML ของ Node นั้น
- InnerText - ข้อมูล Text ที่อยู่ภายใน Tag HTML
ลองใช้งานจริง
- ใส่รายชื่อลงไป จากนั้นกด Grab ครับ
Source Code
- อยู่ที่นี้ครับ (ไม่รับประกันว่าสามารถ Run ใช้งานได้นะครับ ลงดูเหตุผลได้จาก หัวข้อสรุป)
สรุปสิ่งที่ได้ทำ
- เข้าใจการทำงานของ HtmlAgilityPack ครับ แต่มันมีข้อเสียอย่างนึง คือ ไม่ค่อยมี Document ครับ ต้องลองหาๆจาก Google => StackOverflow ครับ
- การดึงข้อมูลจากเว็บยากนะครับ ถ้าดึงแล้ว ถ้าเว็บมีการใส่ Tag แปลกๆมา หรือปรับเปลี่ยนโครงสร้าง Code อาจจะพังได้เลย
- ใช้ DataTable ของ C# นี้ก็มืนเหมือนกันนะ ปกติใช้ List<Object-DTO> เป็นหลัก
- บทความนี้ทำ เพื่อการศึกษาครับ
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.