将Web页面转换成XML数据源(100分)

  • 主题发起人 honestman
  • 开始时间
H

honestman

Unregistered / Unconfirmed
GUEST, unregistred user!
本文将为你提供了一个强大而灵活的方法,旨在从现有的HTML文件中提取并组合成有意义的
数据。
HTML 和Web 已经永远地改变了人们通信与沟通的方式,HTML对人们来说很方便,它使得信
息更易于查看和航行,但遗憾的是,它对计算机之间的交流就远远没有那么方便了:Web页
面上由于层的实现代码,使得计算机系统都难以找到和使用数据。这时,XML应运而生,它
承诺要对计算机系统之间通信的方式起到同样的作用,XML将成为信息在不同计算机系统之
间流动的公用语言。应用本文中介绍的简单编程技术,你可以将任何HTML页面转换成一个
XML数据源。
例程说明
想象有一个花店,每个星期都要根据价格从3个鲜花批发商中的一个进货。每个星期,店主
都要查看每个批发商的Web 站点来找到最低的价格。店主想把这三个批发商的价格信息都
组合到一个Web页面中来简化他搜索的过程。
下面就示范如何从3个 Web页面中提取信息并且将它们结合到一个单一的XML文档中。我们
为这个例子创建了3个鲜花批发商的页面:
<http://www.azur.com/flowershopdemo/fakeflowers.asp>
<http://www.azur.com/flowershopdemo/fictitiousflowers.asp>
<http://www.azur.com/flowershopdemo/pretendflowers.asp>
出于示范目的,每次访问页面时,每个页面上的价格都会改变。另外,由于Web页面经常会
把相关数据放在HTML表格中,样本页面和应用程序将着重从表格中而不是从其它标记中恢复
信息。
解决方案
下表包含了一个含有你需要的数据的样本XML文件Flowers.xml:
<Flowers><Flower><Vendor>Fake Flowers</Vendor><Name>Daffodil</Name>
<Price>$2.00</Price></Flower>
<Flower><Vendor>Fictitious Flowers</Vendor><Name>Daffodil</Name><Price>$5.00</Price>
</Flower>
<Flower><Vendor>Pretend Flowers</Vendor>
<Name>Daffodil</Name><Price>$3.50</Price></Flower>
</Flowers>

现在你需要编写代码,直接从相应的批发商Web 站点中提取鲜花的名称和价格。一个解决方
案是在XML 文档中放置特殊的标记,以后用站点中的值来代替。这种方法与 XSL相同。要想
这样做,你可以定义一个新的元语言,它可让你向XML中增加可替换的标记。
这个新的元语言需要完成以下任务:
识别文档,以便知道它是在使用这个语言
提供一个方法,以指定包含着你想恢复数据的Web页面
指定如何从每个页面恢复特定的数据元素。下面的例子提取前一个XML文件,并且包含了新
的元语言标记来完成上面所列举的3个目标:
<WG:Document xmlns:WG="http://www.azur.com/FlowerShopDemo/WebGatherSchema.xml" >
<Flowers>
<WG:Template url="http://www.azur.com/flowershopdemo/fakeflowers.asp">
<Flower>
<Vendor>Fake Flowers</Vendor>
<Name><WG:GetTableElement pos="1" row="8" col="1"/></Name>
<Price><WG:GetTableElement pos="1" row="8" col="4"/></Price>
</Flower>
</WG:Template>
<WG:Template url="http://www.azur.com/flowershopdemo/fictitiousflowers.asp">
<Flower>
<Vendor>Fictitious Flowers</Vendor>
<Name><WG:GetTableElement pos="1" row="6" col="2"/></Name>
<Price><WG:GetTableElement pos="1" row="6" col="3"/></Price>
</Flower>
</WG:Template>
<WG:Template url="http://www.azur.com/flowershopdemo/pretendflowers.asp">
<Flower>
<Vendor>Pretend Flowers</Vendor>
<Name><WG:GetTableElement pos="1" row="3" col="1"/></Name>
<Price><WG:GetTableElement pos="1" row="3" col="4"/></Price>
</Flower>
</WG:Template>
</Flowers>
</WG:Document>
第二个XML例子有一个包裹元素叫做do
cument,放置在原始XML的两头。Document元素为这
个叫做WebGather的新元语言定义了名称空间。 WebGatherSchema.xml文件中定义了
WebGather 语言元素:
<!—WebGather schema -->
<Schema xmlns="urn:schemas-microsoft-com:xml-data" >
<element type='Document'>
<element type='Template'>
<attribute type='url'/>
<element type='GetTableElement'>
<attribute type='pos'/>
<attribute type='row'/>
<attribute type='col'/>
</element>
</element>
</element>
</Schema>
WebGather schema允许使用三种类型的XML元素:Template(模板)元素只有一个属性 "URL",
它定义了包含数据的源Web页面。GetTableElement标记是一个在Template元素中定义的页面
内表格中一个单元的内容占位符。GetTableElement标记有三个属性,第一个属性叫做"pos" ,
它定义了HTML Web 页面中表格元素的索引号,其中第一个表格就是1;"row"和"col"属性定义
了包含数据的表格中的单元。
具体实现
元语言需要执行才能起作用。我使用了一个 Visual Basic DLL工程文件,其中只包含了一
个类叫做MetaGather。这个类使用了一个公共方法,叫做Transform,它接收一个 XML 字符
串,其中包含WebGather标记,它取代带有来自指定Web 页面值的那些标记,生成XML 字符串。
这个类使用了Microsoft InternetExplorer控件来恢复Web 页面,并从XML字符串参数中的
Template 标记中读取包含数据的页面URL。
Private Function LoadPage _
(ByVal strURL As String) As Boolean
' Initialize thedo
wnload complete flag
m_bDownloadComplete = False
' Load the page to make sure its
' not the cached version
m_IE.Navigate strURL, 4
' Wait untildo
cument finishes loading
While m_IE.ReadyState <> READYSTATE_COMPLETE
DoEvents
Wend
' Check if you ended up on the error page
If m_IE.Document.Title = _
"The page cannot be found" Or _
m_IE.Document.Title = "No page to display" _
then
LoadPage = False
else
LoadPage = True
End If
End Function
LoadPage 函数调用Microsoft Internet控件的Navigate方法从Internet中取回页面内容。
Navigate工作方式是不同步的,因此在继续之前必须要等待页面装载。这可以通过使用一个
循环以等待一个模块级别的标志设置完成来实现。DownloadComplete事件被激活时,这个标
志就被设置。通常Navigate 方法被调用时,DownloadComplete事件就被激活。这样一来,
即使是导航失败,也能保证这个标志最终会被设置为真,然后我们就退出循环。
现在向类中增加以下函数,以便处理WebGather元语言:
Public Function Transform(ByVal sXML As String) As String
Dim Ntemplates As IXMLDOMNodeList
Dim Ntemplate As IXMLDOMElement
Dim Ntables As IXMLDOMNodeList
Dim Ntable As IXMLDOMElement
Dim Nchild As IXMLDOMElement
Dim Ntext As IXMLDOMText
Dim XMLDOM Asdo
MDocument
Dim bLoad As Boolean
Dim sValue As String
' Load the template
Set XMLDOM = CreateObject("Microsoft.XMLDOM")
bLoad = XMLDOM.loadXML(sXML)
' Verify you successfully loaded the XMLdo
cument
If Not bLoad then
Err.Raise vbObjectError + 1000, _
"DataGather.LoadXMLFromTemplate", _
"Invalid XML"
End If
Set Ntemplates = _
XMLDOM.selectNodes("//WG:Template")
For Each Ntemplate In Ntemplates
bLoad = LoadPage _
(Ntemplate.getAttribute("url"))
Set Ntables = NTemplate.selectNodes _
(".//WG:GetTableElement")
For Each Ntable In Ntables
If bLoad then
sValue = GetTableCell _
(Ntable.getAttribute("pos"), _
Ntable.getAttribute("row"), _
Ntable.getAttribute("col"))
else
sValue = "Not Found"
End If
' Create a new text node from the value
Set Ntext = XMLDOM.createTextNode(sValue)
' Replace the current node with
' the new text node
Ntable.parentNode.replaceChild _
Ntext, Ntable
Next
' Remove the template node by assigning all
' its children to the parent node
For Each Nchild In Ntemplate.childNodes
Ntemplate.parentNode.appendChild Nchild
Next
Ntemplate.parentNode.removeChild Ntemplate
Next
' Remove the rootdo
cument node by
' making the first child the root node
Set XMLDOM.documentElement = _
XMLDOM.documentElement.childNodes(0)
Transform = XMLDOM.xml
End Function
Transform函数在strXML 参数之间反复。每当它遇到一个 Template元素,它就装载URL 属
性值所指定的页面,然后用指定表格单元中的数据代替任何子GetTableElement标记。
GetTableCell函数在HTMLdo
M 中反复,直到它找到指定的表格、行和列。
以下是GetTableCell函数:
Private Function GetTableCell(ByVal iTablePos _
As Integer, iRow As Integer, iCol As Integer) _
As String
Dim oElement As Variant
Dim iCount As Integer
On Error GoTo ErrorHandler:
iCount = 1
For Each oElement In _
m_IE.Document.documentElement.All
If UCase(oElement.nodeName) = "TABLE" then
If iCount = iTablePos then
GetTableCell = oElement.rows(iRow - _
1).cells(iCol - 1).innerText
Exit Function
End If
iCount = iCount + 1
End If
Next
ErrorHandler:
GetTableCell = "Not Found"
End Function
测试MetaGather 对象
要想测试 MetaGather对象,你可以创建一个ASP页面,用它来处理列表1中的XML文件。页面
创建一个WebGather对象,将它传递给XML文档字符串,然后应用一个XSL格式表在浏览器中
显示结果。在Web服务器的一个目录中创建一个叫做FlowerShop.asp的ASP页面,可以从浏
览器中访问它。下面是FlowerShop.asp的内容:
<% @Language=VBScript %>
<% Option Explicit %>
<% Response.Expires = 1 %>
<html>
<body>
<h1>Vendor Price Comparison</h1>
<%
' Generate the price comparison table
Dim oDataGather, strXML, oXMLDOM, oXSLDOM
' Load the template file that specifies
' how to gather information
Set oXMLDOM = _
Server.CreateObject("Microsoft.XMLDOM")
oXMLDOM.async = False
oXMLDOM.Load Server.MapPath("Flowers.xml")
' Pass the XML as a string to the DataGather class
Set oDataGather = _
Server.CreateObject("WebGather.XMLMetaGather")
strXML = oDataGather.Transform(oXMLDOM.xml)
Set oDataGather = Nothing
' Load the returned XML into an XMLDOM
'so you can apply an XSL stylesheet
oXMLDOM.LoadXML strXML
' Load the XSL stylesheet
Set oXSLDOM = _
Server.CreateObject("Microsoft.XMLDOM")
oXSLDOM.async = False
oXSLDOM.Load Server.MapPath("Flowers.xsl")
' Apply the XSL and write the result
Response.Write oXMLDOM.transformNode(oXSLDOM)
' Clean up
Set oXMLDOM = Nothing
Set oXSLDOM = Nothing
%>
<B>Press the Refresh Button to get current
prices from the vendor sites.</B>
</body>
</html>
这个ASP页面要引用另外两个页面:Flower.xml和Flowers.xsl。 这些文件必须与ASP页面
位于Web 服务器的同一个目录下。列表1包含了Flower.xml文件,列表2是Flowers.xsl的内
容,它将生成的XML转换成适合在浏览器中显示的HTML表格:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match = "/">
<table border="1"> <tr> <th>Vendor</th> <th>Flower</th> <th>Price</th> </tr>
<xsl:for-each select="//Flowers/Flower"> <tr> <td><xsl:value-of select="Vendor"/>
</td> <td><xsl:value-of select="Name"/></td> <td><xsl:value-of select="Price"/>
</td> </tr> </xsl:for-each> </table> </xsl:template>
</xsl:stylesheet>
当浏览FlowerShop.asp页面时,你会看到一个表格,它显示了来自三个不同批发商站点的
水仙花价格。你可以访问以下地址查看本文整个例子的工作演示:http://www.azur.com/flowershopdemo。
建立功能更加强大的解决方案
本文所描述的方法对于内容相对稳定的Web站点是行之有效的,要修改更加动态的Web页面
则需要一个更加复杂的方案。可以编写代码,通过查找特定的文本或选择一个完整表格中
的内容来扩展选择值,但是如果Web页面的结构改变了,一些非常简单的算法都将被打破。
如果考虑实施更为复杂的方案。可以考虑AZUR (www.azur.com) 的产品eClips,我们可以
使用同样的但却是更加先进的技术来监控动态改变的内容。eClips使用一个拖放界面监控
选中的元素,当该元素改变时将进行警告,直接向移动设备发布信息更新,跟踪选中值的
历史记录。eClips允许任何Web页面都成为一个XML数据源。
 
先学习一下.
 
honestman:
你绝对算称职的版主,教东西还给 分。
我刚刚拜读 你的 xsl 感觉很好
 
我也在学,我也要分
 
多人接受答案了。
 
顶部