2010年11月27日 星期六

在WSH環境如何應用DOM方式分析網頁資料

網頁資料取得後,進一步就是進行網頁剖析,將所需資料取出,以利後續處理。一般來說,取回網頁資料後就是運用善長的程式語言進行字串剖析,取得所需的資料。因此程式語言的字串函數功能越多,分析網頁資料就會越方便。

除了採用字串剖析的方式取得網頁資料,是否有其它方式分析網頁資料?轉個方式想想,在設計動態網頁效果時都是藉由JavaScript程式語言控制DOM(Document Object Model)架構來達到想要的特效,可說DOM(Document Object Model)架構與網頁資料內容是一體兩面的關係;所以,若是採用JavaScrip+DOM的方式進行網頁資料剖析,應該也可以利用 DOM(Document Object Model)架構來取得節點上的資料?!


問題在於網頁資料是在WSH(Windows Script Host)環境下(並非開啟瀏覽器載入網頁),並沒有DOM(Document Object Model)架構,因此首要方向就是要想辦法產生這樣的環境。翻了一下網路,發現這篇「Parsing HTML with JScript」就有寫到我想要的方式,照文章內回覆的說明是使用htmlfile類別在記憶體中建立HTML file。也就是使用ActiveXObject("htmlfile")建立HTML檔案,再將要剖析的網頁資料寫入。

光有HTML檔案還不夠,沒有瀏覽器預設的window、document物件,許多原本習慣的JavaScript語法就無法使用,使用上就不是很方便。另外找到的這篇「Jquery」提到如何使用ActiveXObject("htmlfile")來執行jQuery的功能?雖然jQuery的用法還不嘔熟,但文章中介紹到如何產生 window、document物件,讓DOM(Document Object Model)架構得以完整,這樣組合起來就可以運用JavaScript+DOM剖析網頁的功能了吧?

將以上收集的資料,結合前篇「WSH環境下運用Microsoft.XMLHttp物件抓取網頁資料」介紹的取得網頁資料結果,嘗試進行網頁資料剖析的程式碼如下:
<?xml version="1.0" standalone="yes" encoding="big5"?>

<package>
    <job>
        <script type="text/javascript" language="javascript" charset="utf-8">
        <![CDATA[
            /**
             *
             * 取得資料。
             *
             * @author ace
             *
             * @version 2010/11/11 v0.1
             *
             * @param {String} astrStockCode 股票代碼。
             *
             * @returns {String} 網頁資料。
             *
             * @requires Microsoft.XMLHttp
             *
             */
            function getHttp(astrStockCode) {

                var strReturn = new String("");

                var strURL = new String("http://tw.stock.yahoo.com/q/q?s=" + astrStockCode);

                var objXMLHttp = new ActiveXObject("Microsoft.XMLHttp");
                objXMLHttp.open("GET", strURL, false, "");
                objXMLHttp.send();

                // todo: 加入是否正確取得資料之判斷式。
                var strReturn = new String(objXMLHttp.responseText);

                return strReturn;
            }

            /**
             *
             * 剖析html資料。
             *
             * @author ace
             *
             * @version 2010/11/11 v0.1
             *
             * @param {String} astrTrnDate 交易日期。
             * @param {String} astrStockCode 股票代碼。
             *
             * @returns {Array} 陣列資料。
             *
             */
            function parseHTML(astrTrnDate, astrStockCode, astrContent) {

                var objReturn = new Array();

                /**
                 *
                 * 建立瀏覽器物件。
                 *
                 * @author ace
                 *
                 * @version 2010/11/16 v0.1
                 *
                 * @see <a href="http://us.generation-nt.com/answer/parsing-html-jscript-help-59686112.html">Parsing HTML with JScript</a>
                 * @see <a href="http://www.eggheadcafe.com/software/aspnet/36239301/jquery.aspx">Jquery</a>
                 *
                 */
                var htmlfile = new ActiveXObject("htmlfile");
                var window = htmlfile.parentWindow;
                var navigator = window.navigator;
                var document = window.document;
                var location = document.location;

                window.ActiveXObject = {};

                // todo:載入html檔案發生錯誤時,無法被JavaScript的catch語法攔截?!
                htmlfile.open();
                htmlfile.write(astrContent);
                htmlfile.close();

                var objTR = document.getElementsByTagName("tr");

                for (var intIndex = 0; intIndex < objTR.length; intIndex++) {

                    var objTD = objTR[intIndex].getElementsByTagName("td");

                    if (objTD.length == 12) {
                        objReturn.push(astrTrnDate);
                        objReturn.push(astrStockCode);
                        objReturn.push(objTD[2].firstChild.innerHTML);  // 成交金額多了<b>標籤,再往下一個結點取資料。
                        objReturn.push(objTD[3].innerHTML);
                        objReturn.push(objTD[4].innerHTML);
                        objReturn.push(objTD[6].innerHTML.replace(",", ""));
                        objReturn.push(objTD[7].innerHTML);
                        objReturn.push(objTD[8].innerHTML);
                        objReturn.push(objTD[9].innerHTML);
                        objReturn.push(objTD[10].innerHTML);

                        break;
                    }
                }

                return objReturn;
            }

            // Program Start Here
            try {
                var objToday = new Date();

                // 以系統日期當作交易日期,理想作法應以網頁記載時間為準。
                var strTrnDate = new String("");
                strTrnDate = objToday.getFullYear().toString() + (objToday.getMonth() + 1).toString() + objToday.getDate().toString();

                var strStockCode = new String("2002");

                var objResult = parseHTML(strTrnDate, strStockCode, getHttp(strStockCode));

                if (objResult.length != 0) {
                    WScript.Echo("交易日期:" + objResult[0]);
                    WScript.Echo("股票代碼:" + objResult[1]);
                    WScript.Echo("成交價格:" + objResult[2]);
                    WScript.Echo("買進價格:" + objResult[3]);
                    WScript.Echo("賣出價格:" + objResult[4]);
                    WScript.Echo("成交張數:" + objResult[5]);
                    WScript.Echo("昨日收盤:" + objResult[6]);
                    WScript.Echo("開盤價格:" + objResult[7]);
                    WScript.Echo("最高價格:" + objResult[8]);
                    WScript.Echo("最低價格:" + objResult[9]);
                }
            }
            catch (e) {
                WScript.Echo("執行過程有誤,錯誤訊息:" + e.description);
            }
        ]]>
        </script>
    </job>
</package>
只是!!上面程式執行後出現「錯誤:必須要有物件」的訊息,依指定的行數去查詢後發現該行是一行JavaScript函數,只是該函數定義在另一個檔案中。嘗試改取沒有JavaScript語法的網頁,是確實可以順利的剖析網頁資料。

努力翻了些資料,發現這篇「How can I automatically ignore script error parsing JScript?」的提問者也遇到同樣的狀況,也有人回覆處理的方式,提到可以針對window.onerror這個事件進行處理來解決問題,只是並沒有寫出作法,所以暫時卡在這裡了!!

沒有留言:

張貼留言

Related Posts Plugin for WordPress, Blogger...