級別: 中級
Edd Dumbill, 編輯兼發(fā)行人, xmlhack.com
2003 年 3 月 01 日
當(dāng)
您開始聚集來自 Web 的數(shù)據(jù)時,了解其來源是至關(guān)重要的。本文中,Edd Dumbill 研究了 Redland
資源描述格式(Resource Description Format,RDF)應(yīng)用程序框架的上下文特性并創(chuàng)建了作為演示的一個 RDF Site
Summary(RSS)1.0 聚集器。
一年前,我為
developerWorks寫了兩篇有關(guān)
朋友的朋友(Friend-of-a-Friend,F(xiàn)OAF)項(xiàng)目的文章。FOAF 是一種 XML/RDF 詞匯表,用于以計算機(jī)可讀的形式描述您通常可以放在主 Web 頁上的某種個人信息,如您的姓名、即時信使昵稱和工作地點(diǎn)等。
在我有關(guān) FOAF 的第二篇文章(請參閱
參考資料)的清單 6 中,我演示了
FOAFbot,一個我編寫的聚集人們的 FOAF 文件并回答相關(guān)問題的社區(qū)支持代理。FOAFbot 能夠記錄誰說了關(guān)于誰的什么事情。當(dāng)問及我叫什么名字時,F(xiàn)OAFbot 答道:
edd@xml.com's name is 'Edd Dumbill', according to Dave Beckett, Edd Dumbill, Jo Walsh, Kip Hampton, Matt Biddulph, Dan Brickley, and anonymous source Anon47
|
FOAFbot 背后的思想是,如果您能夠驗(yàn)證不同的幾個人(您所信任的人)都記錄了一個事實(shí),那么您很可能相信這是真實(shí)的。
以
下是追溯這樣的元數(shù)據(jù)源頭的另一個運(yùn)用。搜索引擎在其早期歷史中被濫用的一個主要表現(xiàn)是惡意元標(biāo)記(meta tag
spamming)。網(wǎng)站會把錯誤的元數(shù)據(jù)放到它們的頁面中以提高它們的搜索引擎排名。由此,搜索引擎不再關(guān)注元標(biāo)記,因?yàn)樗鼈儤O有可能提供虛假信息。事
實(shí)上,象 Google 這樣的搜索引擎找到了其它更高級的度量來評定頁面關(guān)聯(lián)性。
展望 Web 的未來,避免諸如惡意元標(biāo)記這樣的濫用將變得至關(guān)重要。Tim Berners-Lee 對 Semantic
Web 的展望(請參閱
參考資料)是希望出現(xiàn)這樣一個 Web:其上大多數(shù)的數(shù)據(jù)都是機(jī)器可讀的,從而使得目前由人類完成的大多數(shù)信息處理自動進(jìn)行。
元數(shù)據(jù)濫用在 Semantic Web 上可能造成的麻煩甚至更大:網(wǎng)站不再限于僅僅造自己站點(diǎn)的假。它還可以造其它站點(diǎn)的假。例如,一家書店對競爭者的報價作假,這是有可能的。
我不會探究各種安全性和可信機(jī)制(這些機(jī)制將防止這種語義的破壞行為)的細(xì)節(jié),而是將重點(diǎn)討論這樣的機(jī)制之所以成為可能的基礎(chǔ):
追溯源頭。
存儲 RDF
處理 RDF 然后存儲它的應(yīng)用程序使用
三元組存儲(triple store)來做到這一點(diǎn)。RDF/XML 輸入文檔被分解成一列包含
主語、謂語和賓語
的三元組。然后后續(xù)處理就是操作和查詢存儲中的三元組。只有在涉及到交換時,才會用 RDF 的 XML 語法。由于將數(shù)據(jù)直接分解成三元組,所以
XML 工具(象 XPath 或 XQuery)就沒有多少用處了。已經(jīng)有人編寫了直接使用 XQuery 操作 XML 語法的 RDF
處理工具,但我認(rèn)為對于通用的 RDF 處理,這多少有點(diǎn)多此一舉。
為了進(jìn)行演示,我將向您展示如何將簡單的 RSS 1.0 文檔用作測試數(shù)據(jù)。最近我建立了一個 weblog 站點(diǎn),其中,我對不知情的公眾強(qiáng)加了我的觀點(diǎn)。為了將我涂鴉式的元數(shù)據(jù)放在一起,我生成了一個 RSS 1.0 文件(請參閱
參考資料獲取鏈接)。該文件的開始部分類似于清單 1,曾接觸過 RSS 的任何人應(yīng)該對它非常熟悉。
清單 1. 摘自 RSS 1.0 文件的開始部分
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/" > <channel rdf:about="http://usefulinc.com/edd/blog"> <title>Edd Dumbill's Weblog: Behind the Times</title>
<description> Thoughts and comment from Edd Dumbill, technology writer and free software hacker. </description> <link>http://usefulinc.com/edd/blog</link>
|
在 RDF 處理器將這一部分解析成三元組時,我獲得了清單 2 中顯示的數(shù)據(jù)。
清單 2. 與清單 1 的開始部分對應(yīng)的 RDF 三元組
[http://usefulinc.com/edd/blog, http://www.w3.org/1999/02/22-rdf-syntax-ns#type, http://purl.org/rss/1.0/channel] [http://usefulinc.com/edd/blog, http://purl.org/rss/1.0/title, "Edd Dumbill's Weblog: Behind the Times"] [http://usefulinc.com/edd/blog, http://purl.org/rss/1.0/description, "Thoughts and comment from Edd Dumbill, technology writer and free software hacker."] [http://usefulinc.com/edd/blog, http://purl.org/rss/1.0/link, "http://usefulinc.com/edd/blog"]
|
隨后 RDF 應(yīng)用程序所要操作的就是
清單 2中顯示的這列三元組了。至此,一切都很順利。但在存儲這個數(shù)據(jù)時,您會丟失一些重要信息的線索,即數(shù)據(jù)的出處和其它相關(guān)數(shù)據(jù),如我是何時對數(shù)據(jù)拍了快照。要記錄這個信息,我需要通過一些方式使該信息與我所找到的 RDF 語句相關(guān)聯(lián)。
首先,對于
清單 2中的數(shù)據(jù),我需要模擬出一個描述,記錄下對于這些數(shù)據(jù)我可能要說的內(nèi)容。清單 3 就包含了這樣一個示例描述,它使用了專為這一目的而虛構(gòu)出的一個示例名稱空間。
清單 3. 檢索清單 1 和清單 2 中數(shù)據(jù)的示例描述
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:me="http://example.org/ns/mymeta#">
<rdf:Description rdf:about="http://example.org/retrieval/52365"> <me:origin rdf:resource="http://usefulinc.com/edd/blog/rss" /> <me:retrieved>2003-06-24T08:59:55.00Z</me:retrieved> </rdf:Description> </rdf:RDF>
|
您可以從
清單 3中看出:我已經(jīng)(任意)虛構(gòu)了一個 URI 來表示對一個文件進(jìn)行的第 52365 次檢索。這似乎是一個合理的方法,如同任何對遠(yuǎn)程資源的每次輪詢所命名的那樣。這個資源的出處是這個 RSS 1.0 文件的 URI,而時間戳記表明何時找到它的。
現(xiàn)在,剩下的就是存儲四元組,而不是存儲三元組。清單 4 展示了我如何修改
清單 2以顯示我現(xiàn)在添加到存儲中的所有數(shù)據(jù)。
清單 4. 增加了上下文 URI 和上下文元數(shù)據(jù)的三元組
[http://usefulinc.com/edd/blog, http://www.w3.org/1999/02/22-rdf-syntax-ns#type, http://purl.org/rss/1.0/channel, http://example.org/retrieval/52365] [http://usefulinc.com/edd/blog, http://purl.org/rss/1.0/title, "Edd Dumbill's Weblog: Behind the Times", http://example.org/retrieval/52365] [http://usefulinc.com/edd/blog, http://purl.org/rss/1.0/description, "Thoughts and comment from Edd Dumbill, technology writer and free software hacker.", http://example.org/retrieval/52365] [http://usefulinc.com/edd/blog, http://purl.org/rss/1.0/link, "http://usefulinc.com/edd/blog", http://example.org/retrieval/52365] [http://example.org/retrieval/52365, http://example.org/ns/mymeta#origin, http://usefulinc.com/edd/blog/rss, <NULL>] [http://example.org/retrieval/52365, http://example.org/ns/mymeta#retrieved, "2003-06-24T08:59:55.00Z", <NULL>]
|
清單 4中演示的思想是每個 RDF 語句都增加了一個 URI,它將該 RDF 語句鏈接到我想要存儲的有關(guān)該 RDF 語句的元數(shù)據(jù)上。這個簡單機(jī)制給我?guī)砹藷o窮的力量。除了使我能夠檢索有關(guān)該 RSS 文件的元數(shù)據(jù)以外,它還提供了從存儲中除去該信息的便利方法。
實(shí)際實(shí)現(xiàn)
在運(yùn)用這個思想時,我已將上面四元組的第四個元素稱為
上下文。這個術(shù)語并不規(guī)范,但它對我而言非常有效。而且,Redland RDF 應(yīng)用程序框架中也使用了這個術(shù)語,我將使用該框架來演示這個追溯源頭的應(yīng)用程序。
(順
便提一下,我要對 Dave Beckett(Redland 的創(chuàng)建者)表示感謝。在我去年編寫 FOAFbot 時,Redland
不支持上下文,所以我最終只能以一種非常繁冗的格式來實(shí)現(xiàn)上下文。Dave 回應(yīng)了我的請求,他將上下文的支持添加到了他的工具箱中。)
Redland
是一個基于 C 的工具箱,帶有許多語言綁定,包括 Python、Perl 和 Java。它由一個 RDF 解析器、raptor
和一個數(shù)據(jù)存儲組成。該存儲目前使用 Berkeley DB 文件,不過還在開發(fā)對底層 SQL 存儲的支持。對于我的示例而言,我將使用到
Redland 的 Python 綁定。
這里的目標(biāo)是要創(chuàng)建一個簡單的 RSS 1.0
聚集器。使用聚集器的目的是對多個 RSS 饋送進(jìn)行重復(fù)拍快照,并允許您以有趣的方式組合它們。隨著新內(nèi)容項(xiàng)的加入,RSS
文件會隨著時間的變化而發(fā)生更改 - 我想避免多個冗余項(xiàng),而仍能保留歷史項(xiàng)。在本文后面,我將開發(fā)所需的一些功能。
您將在
參考資料中找到一個到這個項(xiàng)目的 Python 代碼(fraggle.tar.gz)的鏈接。還有一個到 Redland 工具箱的鏈接,您也需要安裝這個工具箱。
Aggregator
類會處理訪存和存儲 RDF 數(shù)據(jù)這個有趣的工作。清單 5 摘自這個類的
load_uri
方法,它從
aggregate.py
的第 189 行開始。
清單 5. 追溯檢索到的 RDF 的上下文
stream = self._parser.parse_as_stream( RDF.Uri(string="file:./%s" % fname), base_uri=urinode.uri) if stream: channel = None
context = self.context_uri_node() timestamp = RDF.Node(literal= time.strftime("%Y-%m-%dT%H:%M:%S.00Z")) while not stream.end(): statement = stream.current() # add the statement to the model, with context
self._model.add_statement(statement, context)
# if it's a <rss:channel> remember the URI if ( statement.predicate == _rdfType and statement.object == _rssChannel ): channel = RDF.Node(node=statement.subject)
# move on stream.next()
# now to add the context information # first, the source URI
self._model.add_statement(RDF.Statement( subject=context, predicate=_fraggieSource, object=urinode), _globalContext) # second, the channel URI
self._model.add_statement(RDF.Statement( subject=context, predicate=_fraggieChannel, object=channel), _globalContext) # third, the timestamp
self._model.add_statement(RDF.Statement( subject=context, predicate=_fraggieTimestamp, object=timestamp), _globalContext) # fourth, the checksum
self._model.add_statement(RDF.Statement( subject=context, predicate=_fraggieChecksum, object=RDF.Node(literal=checksum)), _globalContext) self.register_fetch(urinode, context)
|
該清單中用粗體顯示的幾行與追溯上下文有關(guān)。首先,使用
context_uri_node()
生成上下文的 URI。這返回形式為
http://usefulinc.com/fraggie/global/1
的 URI 結(jié)果。這個上下文然后被追加到在檢索到的 RSS 中找到的每個語句中。一旦存儲了 RSS 數(shù)據(jù),我隨后就將有關(guān)上下文 URI 的數(shù)據(jù)添加到存儲中。在本例中,我存儲了作為 RSS 文件來源的 URI、RSS 通道本身的 URI(
rss:channel
元素中的
rdf:about
值)、訪存文件的時間以及文件的 MD5 校驗(yàn)和(以便以后確定 RSS 文件是否隨時間變化而發(fā)生了更改)。
如果您下載了源代碼,那么正如您將看到的,
Aggregator
類的其余部分實(shí)現(xiàn)了兩個功能:第一個是 RSS 搜索所需的內(nèi)務(wù)處理方法;第二個向聚集器的詢問者提供了查詢方法。請注意,所有的內(nèi)務(wù)處理變量(如
fetch
計數(shù))都是用 RDF 表示的,并且保存在 RDF 存儲中。清單 6 展示了將計數(shù)變量表示為 RDF/XML 語句。
清單 6. 用 RDF 表示的內(nèi)部計數(shù)器
<rdf:Description rdf:about="http://usefulinc.com/fraggie/counter" rdf:value="0" />
|
我發(fā)現(xiàn)在 RDF 存儲中持久地保存任何擁有全局作用域的變量是很有意義的。這種方法的一個直接優(yōu)點(diǎn)是它在多次調(diào)用后仍能保持狀態(tài)。
該演示歸檔文件包括兩個您可以運(yùn)行的示例 RSS 文件。使用
python fraggle.py
可以調(diào)用該演示。該演示首先使聚集器裝入這兩個示例 RSS 文件,它們都引用了 Mark Pilgrim 最近在 XML.com
上發(fā)表的一篇文章。這個練習(xí)的目的是找到誰對這篇文章作了什么評價。運(yùn)行該演示產(chǎn)生的輸出如清單 7
所示(為可讀性起見,已對一些輸出行重新進(jìn)行了格式處理)。
清單 7. fraggle.py 的輸出
Links to http://www.xml.com/pub/a/2003/07/02/dive.html
From : Meerkat: An Open Wire Service: XML.com <http://meerkat.oreillynet.com/> Time : 2003-07-05T15:39:26.00Z Title: The Vanishing Image: XHTML 2 Migration Issues Desc : In Mark Pilgrim's latest Dive Into XML column, Pilgrim examines XHTML 2.0 <tt>object</tt> element, which is a replacement for the more familiar and widely supported <tt>img</tt>.
From : paranoidfish.org/links <http://www.paranoidfish.org/links/> Time : 2003-07-05T15:39:25.00Z Title: XML.com: The Vanishing Image: XHTML 2 Migration Issues [Jul. 02, 2003] Desc : using <object> as a replacement for <img> is not a safe bet right now
|
摘自 XML.com 的第一部分展示了對這篇文章的正式描述。第二部分摘錄展示了由 paranoidfish.org 網(wǎng)站所有者提供的摘要。
如果您研究過
fraggle.py
的源代碼,那么將明白所有查詢都圍繞上下文進(jìn)行。首先,會詢問聚集器哪些上下文提到了這篇文章的
URI。然后根據(jù)上下文請求文章元數(shù)據(jù)并建立索引。請注意,上下文在時間上對應(yīng)于一個饋送的快照。如果我的示例隨時間變化要獲得更多 RSS
文件的快照,那么您可能會看到有幾項(xiàng)都是來自同一來源的 - 可能在描述上稍作了更改(人們常常會隨時修正拼寫錯誤)。對 fraggle.py
中查詢的一個明顯改進(jìn)是根據(jù)源進(jìn)行分組,而不是單單根據(jù)時間順序進(jìn)行顯示。
盡
管從瀏覽、自我沖浪(ego-surfing)的角度而言,weblog 和其它因特網(wǎng)站點(diǎn)的 RSS
饋送很有趣,但我認(rèn)為象這樣一個項(xiàng)目的真正價值很有可能體現(xiàn)在企業(yè)中。許多組織常常會生成大量以時間為序的數(shù)據(jù)流。舉一個簡單的示例,URI
分配給了客戶或項(xiàng)目,然后就可以生成并聚集活動的 RSS 流。
然后可以很輕松的為那些對這樣的聚集數(shù)據(jù)感興趣
的任何人劃分和切割這些數(shù)據(jù)。例如,管理者可能希望知道每個工人在做什么,項(xiàng)目經(jīng)理可能希望獲得最近的三個狀態(tài)更新,更高級的管理層可能希望獲得整個部門
的快照視圖,等等。不難想象,這種工具很可能會在客戶關(guān)系管理(CRM)這一領(lǐng)域中產(chǎn)生最佳收益。
結(jié)束語
本文中演示的簡單示例只觸及了追溯 RDF 源頭的皮毛。在 Web 上,信息來自哪里與信息本身同樣重要。源頭追溯的 RDF 工具剛開始興起,當(dāng)它們的使用變得更廣泛時,它們的能力無疑會更加完善。
毫無疑問,Redland RDF 應(yīng)用程序框架是一個值得進(jìn)一步研究的工具箱。它具有到您最喜愛的腳本編制語言的接口;它運(yùn)行在 UNIX、Windows 和 Mac OS X 上;而且它是一個開放源碼項(xiàng)目,所以您所做的任何改進(jìn)會使整個社區(qū)受益。
參考資料
|
|
關(guān)于作者