程序員面臨的一個共同任務就是收集 Web 站點的數據,並將它分佈到數據庫或其他 Web 頁。例如,程序員可能需要從氣象站點獲得天氣預報圖,從在線股票經紀人那裏獲得股票報價,以及從新聞站點獲得行業新聞。然後,這些信息被放在一個 Web 頁上,供 CIO、商人或銷售經理使用。或者,也許程序員需要跟蹤歷來的氣象資料,並需要每天將來自氣象站的天氣預報信息存入數據庫。其應用不勝枚舉。
過去,這些選擇相當受限制。現在,通過使用象 WinInet.dll 這樣的 HTTP 組件或許多其他第三方組件,您就可以獲取 Web 頁,並利用幾百種字符串處理功能來獲得網頁中您所感興趣的部分。這一技術已在應用,但很不理想。如果您致力於計算機科學(或者有足夠的時間),就會爲 HTML 創建一個分析器,以標記 Web 頁,然後分析您需要的網頁部分。不過,由於 Internet Explorer 的體系結構中已包含了可重複使用的用分析器,這些都不需要了。
Internet Explorer 不只是一個程序,更是許多可重複使用組件的集合與容器。在拆取 Web 頁時,最有意思的兩個組件是 shdocvw.dll 和 mshtml.dll。第一個組件 shdocvw.dll,包含稱爲 WebBrowser 的 Microsoft(R) ActiveX(R) 控件,它真實地顯示 Web 頁。在運行 Internet Explorer 時,顯示 Web 頁的主窗口就是這樣的控件。第二個組件 mshtml.dll,含有能分析 WebBrowser 控件中所包含文檔的 HTML 分析器。
可能有這種情況,在您的應用程序內部,已經用 WebBrowser 控件來駐留 Web 頁,但仍需要重新創建一個小瀏覽器來啓動 Web 頁的拆取。
- 在文件菜單上,請單擊新建工程,以創建“標準 EXE”,然後在工程菜單上單擊部件,以添加 Microsoft HTML Object Library 和 Microsoft Internet Controls。(見圖 1。)
圖 1.
- 在工具箱中,可看見 WebBrowser 組件。拖動其中之一,文本框和主窗體上的命令按鈕。將此文本框的 Text 屬性設置爲 “http://moneycentral.msn.com/”,將此命令按鈕的 Caption 屬性設置爲“瀏覽(&B)”。(見圖 2。)
圖 2.
- 雙擊該命令按鈕,然後在事件處理器中放入下列代碼,導航至文本框中命名的 Web 站點:
Private Sub Command1_Click() WebBrowser1.Navigate Text1.TextEnd Sub
- 保存並運行該程序。試着按瀏覽按鈕,導航到文本框中指定的站點。您已經創建了一個基本的 Web 瀏覽器 — 就其本身而言沒什麼用,甚至沒什麼意義,但它卻是邁向 Web 拆取技術的第一步。
- 回到工程中,在代碼窗口中選擇 WebBrowser1 對象,然後選擇 DocumentComplete 的事件處理器。一旦整個 Web 頁下載到此瀏覽器中,即觸發該事件:
Private Sub WebBrowser1_DocumentComplete_ (ByVal pDisp As Object, URL As Variant)End Sub
傳遞到該事件中的 URL 就是我們導航所至的位置,它在日後確定瀏覽器所在的頁面時將更爲有用。WebBrowser 控件有一個屬性稱爲 Document(文檔),可將其視爲 IHTMLDocument 來處理:
Private Sub WebBrowser1_DocumentComplete(_ ByVal pDisp As Object, URL As Variant) Dim Doc As IHTMLDocument2 Set Doc = WebBrowser1.Document //下一步:分析該文檔End Sub
較新的 IHTMLDocument2 具有 IHTMLDocument 中無法使用的特性。可對系統使用 IHTMLDocument 替代老版本的 Internet Explorer,如果您有勇氣的話,甚至可以使用 IHTMLDocument3。補充說明一下,我們假設您已經導航到 Word 文檔或 XML 文檔,而非 HTML 文檔。不要將變量 doc 聲明爲 IHTMLDocument2,可將其聲明爲 Word 的文檔或 XML 的 DOMDocument。
在進行下一步之前,理解 HTML 文檔的結構是非常重要的。和 XML 不一樣,HTML 文檔的組合有一定的自由度。例如,您會遇到未關閉標記的 HTML 文檔。HTML 文檔確實有某種結構。結構好的 HTML 文檔通常具有下列元素:
<HTML> <HEAD> header information like the <TITLE> </HEAD> <BODY> elements like <TABLE> and <A> and <IMG> </BODY></HTML>
請注意 HTML 的樹狀結構。標記包含標記又包含標記,如此等等。特別是,每一個標記元素都包含一個 0 到 n 個標記元素的集合。<TABLE> 標記可以包含 <TR> 標記。每個 <TR> 標記可以包含 <TD> 標記,後者又可以包含其他標記如錨或圖像等。
- 現在,分析整個 http://moneycentral.msn.com/,並在帶 MSFT 符號的頁填上第二個 <INPUT> 標記。然後,調用此窗體上的提交:
Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant) Dim doc As IHTMLDocument2 Set doc = WebBrowser1.Document If URL = _ "http://moneycentral.msn.com/home.asp" Then '填充帶輸入標記的元素集合 Dim Inputs As IHTMLElementCollection Set Inputs = doc.All.tags("INPUT") '選擇第一個輸入標記 Dim Element As IHTMLElement Set Element = Inputs.Item(1, 1) '使用正確的界面 Dim InputElement As IHTMLInputElement Set InputElement = Element InputElement.Value = Text1.Text '調用此頁第一個窗體上的提交 doc.Forms.Item(0, 0).submitEnd Sub
在此您會看到,標記集合如何包含可視爲其特定類型的標記。每一個標記都可用 IHTMLElement 界面表示,或用指定爲該標記類型的界面表示。例如,<TABLE> 標記可用 IHTMLTableElement 或 IHTMLElement 表示。
標記的集合都包含下列重要的方法和屬性:
- 長度。可將其理解爲計數,或集合中項目的數量。
- 項目。用於選擇集合中的特殊元素。“項目”有兩個參數,第二個參數即命名的標記。
- 標記。將要過濾的元素傳遞給標記。標記 ("A") 將返回集合內所有錨的集合。要想有效地拆取頁,就需要學會使用標記集合。
現在可能您會問,“爲什麼不直接轉到 http://moneycentral.msn.com/scripts/webquote.dll?ipage=qd&Symbol=msft?”當然是可以的,但這個例子告訴大家如何在更復雜的情況下操縱 HTML 窗體。
如果您未做進一步的改動即運行該程序,就會注意到它將陷入無休止的循環,沒完沒了地下載同一個頁面。程序不斷地尋找要填充的窗體,並反覆調用 DocumentComplete。要修正這個缺陷,應在 DocumentComplete 中置入一些邏輯,告訴分析器,只有在正確的頁面上才提交窗體。
- 長度。可將其理解爲計數,或集合中項目的數量。
- 接下來,讓我們放入這個邏輯,並引入實際的股票報價。另外,我們不捕獲文本框中的 URL,而是捕獲股票符號:
Private Sub Command1_Click() WebBrowser1.Navigate _ "http://moneycentral.msn.com/home.asp"End Sub Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant) Dim doc As IHTMLDocument2 Set doc = WebBrowser1.Document If URL = "http://moneycentral.msn.com/home.asp" Then '填充帶輸入標記的元素集合 Dim Inputs As IHTMLElementCollection Set Inputs = doc.All.tags("INPUT") '選擇第一個輸入標記 Dim Element As IHTMLElement Set Element = Inputs.Item(1, 1) '使用正確的界面 Dim InputElement As IHTMLInputElement Set InputElement = Element InputElement.Value = Text1.Text '調用該頁第一個窗體上的提交 doc.Forms.Item(0, 0).submit ElseIf URL = _ "http://moneycentral.msn.com/scripts/webquote.dll?ipage=qd&Symbol=" _ & Text1.Text Then Dim Tables As IHTMLElementCollection Set Tables = doc.All.tags("TABLE") '獲得第 14 個表的第二個項目(基於 0) Dim Quote As IHTMLElement Set Quote = _ Tables.Item(14, 14).All.tags("TD").Item(2, 2) '顯示開始標記和結束標記之間的文本 MsgBox Quote.innerText End IfEnd Sub
圖 3.
到了這最後一步,自定義的瀏覽器已被轉入有效的 Web 拆取器。重要的是,要注意有了 IHTMLElement 之後獲得文本的可用選項。有 4 個屬性:
- innerText:開始標記和結束標記之間的文本。
- innerHTML:開始標記和結束標記之間的文本和 HTML。
- outerText:對象的文本。
- outerHTML:對象的文本和 HTML。
還要注意從第 4 個表(基於 0)的第 11 個元素中檢索到的最終報價字符串。如果 MoneyCentral? 決定重新調整該頁怎麼辦?您最好的策略是根據合理的假定來查詢頁面。如果您知道報價幾乎總是放在新聞標題的前面,那麼就從新聞標題往回查詢那個表。還有一種策略是,當更改頁面的格式時,有一種簡單的方法來更新分析器。一種方法就是將分析的職能細分爲較小的組件。每個組件可以實現一個預定義的界面,接受要分析的 IHTMLDocument。與實際的 Web 頁失去同步的分析組件可被替換。這樣帶來的好處是,多個編程人員都可以編寫分析器,只需給定要實現的界面和要拆取的 Web 站點即可。
爲了避免複雜,將 IHTMLDocument 從 DocumentComplete 函數傳遞 COM DLL,後者可以分析 IHTMLDocument 並返回想要的有效負載。這有利於程序的模塊化,並易於更新與 Web 站點失去同步的分析部分。它還使多個開發者能同時處理這個項目,因爲他們有一個乾淨的界面來編寫分析器。
- innerText:開始標記和結束標記之間的文本。
在把新的程序推向市場以前,還有幾個實際問題要考慮。首先,很可能 MoneyCentral 和其他許多站點不願意別人下載他們的內容,也不喜歡看廣告。您可能得與摘取其內容的站點簽訂一份協議。
還有很重要的一點要注意,即如果您是 Web 站點的操作員,那麼還有更好的辦法將您的內容提供給其他系統。雖然可以讓其他人來拆取您的 Web 頁,但這仍很笨拙。還有一個更好的方法是,提供 XML 來表現內容。並且,隨着 XML 被廣泛採用,Web 站點開始提供其數據的 XML 表現形式以及 HTML 界面,也不值得大驚小怪。在這樣的時刻到來之前,您也許還得拆取 Web 頁。Web 頁的拆取往往失之笨拙,但 Microsoft HTML 分析器可令其稍微好一些。