這篇文章主要講解了“Twitter是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Twitter是什么”吧!
創(chuàng)新互聯(lián)專注于扶綏企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè),商城網(wǎng)站建設(shè)。扶綏網(wǎng)站建設(shè)公司,為扶綏等地區(qū)提供建站服務(wù)。全流程按需規(guī)劃網(wǎng)站,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
Twitter 迅速占領(lǐng)了 Internet 市場(chǎng)。您肯定知道這個(gè)出色的社交網(wǎng)絡(luò)工具允許訂閱者提供關(guān)于他們自身以及當(dāng)前正在執(zhí)行的任務(wù)的簡要狀態(tài)更新。追隨者 將接收到他們的 “Twitter 提要” 的更新,這與博客將更新生成到博客閱讀者的提要中極為類似。
就其本身而言,Twitter 是對(duì)社交網(wǎng)絡(luò)的有趣討論,并且是用戶之間的新一代 “高度互聯(lián)”,它具備您能想到的所有優(yōu)點(diǎn)和缺點(diǎn)。
由于 Twitter 很早就發(fā)布了其 API,因此大量 Twitter 客戶機(jī)應(yīng)用程序涌入到 Internet 上。由于該 API 主要建立在直觀和易于理解的基礎(chǔ)上,因此許多開發(fā)人員都發(fā)現(xiàn)有必要構(gòu)建一個(gè)自己的 Twitter 客戶機(jī),這與學(xué)習(xí) Web 技術(shù)的開發(fā)人員構(gòu)建自己的博客服務(wù)器極為類似。
考慮到 Scala 的功能性(這看上去能很好地協(xié)同 Twitter 的 REST 式特性)以及非常出眾的 XML 處理特性,因此嘗試構(gòu)建一個(gè)用于訪問 Twitter 的 Scala 客戶機(jī)庫應(yīng)該是一個(gè)非常不錯(cuò)的體驗(yàn)。
何為 Twitter?
在詳細(xì)討論之前,我們先來看看 Twitter API。
簡單來說,Twitter 是一個(gè) “微型博客” — 關(guān)于您自己的簡短個(gè)性化提要,不超過 140 個(gè)字符,任何 “追隨者” 都可以通過 Web 更新、RSS、文本消息等方式接收它們。(140 字符的限制完全來自文本消息,它是 Twitter 的主要來源渠道,并受到類似的限制)。
最具 REST 特征 是什么意思?
一些讀者會(huì)對(duì)我所使用的最具 REST 特征 短語感到好奇;這需要一些說明?!癟witter API 試圖符合 Representational State Transfer (REST) 的設(shè)計(jì)原則”。并且在很大程度上說它做到了。該思想的創(chuàng)造者 Roy Fielding 可能不同意 Twitter 使用這個(gè)術(shù)語,但實(shí)現(xiàn)來說,Twitter 的方法將適合大多數(shù)人的 REST 定義。我只希望避免關(guān)于 REST 定義的激烈爭論。因此,我使用了限定詞 “最”。
從實(shí)際的角度來說,Twitter 是一個(gè)最具 REST 特征 的 API,您可以使用一些種類的消息格式 — XML、ATOM、RSS 或 JSON — 來發(fā)送或從 Twitter 服務(wù)器接收消息。不同的 URL,與不同的消息和它們所需及可選的消息部分相結(jié)合,可以發(fā)起不同的 API 調(diào)用。例如,如果您希望接收 Twitter 上所有人的所有 “Tweets”(Twitter 更新)的完整列表(也稱作 “公共時(shí)間軸”),您需要準(zhǔn)備一個(gè) XML、ATOM、RSS 或 JSON 消息,將它發(fā)送給合適的 URL,并采用與 Twitter 網(wǎng)站(apiwiki.twitter.com)上相同的格式來使用結(jié)果:
------------------------------------------------------------
public_timeline
返回設(shè)定了自定義用戶圖標(biāo)的
非保護(hù)用戶的 20 條最新狀態(tài)。不需要身份驗(yàn)證。
注意,公共時(shí)間軸將緩存 60 秒鐘
因此頻繁請(qǐng)求它不再浪費(fèi)資源。
URL: http://twitter.com/statuses/public_timeline.format
格式:xml、json、rss、atom
方法:GET
API 限制:不適用
返回:狀態(tài)元素列表
------------------------------------------------------------
從編程的角度來說,這意味著我們給 Twitter 服務(wù)器發(fā)送一個(gè)簡單的 GET HTTP 請(qǐng)求,并且我們將獲取一組封裝在 XML、RSS、ATOM 或 JSON 消息中的 “狀態(tài)” 消息。Twitter 站點(diǎn)將 “狀態(tài)” 消息定義為類似清單 1 所示的內(nèi)容:
清單 1. 您好世界,您在哪里?
< feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"> < title>Twitter / tedneward< /title> < id>tag:twitter.com,2007:Status< /id> < link type="text/html" rel="alternate" href="http://twitter.com/tedneward"/> < updated>2009-03-07T13:48:31+00:00< /updated> < subtitle>Twitter updates from Ted Neward / tedneward.< /subtitle> < entry> < title>tedneward: @kdellison Happens to the best of us...< /title> < content type="html">tedneward: @kdellison Happens to the best of us...< /content> < id>tag:twitter.com,2007:http://twitter.com/tedneward/statuses/1292396349< /id> < published>2009-03-07T11:07:18+00:00< /published> < updated>2009-03-07T11:07:18+00:00< /updated> < link type="text/html" rel="alternate" href="http://twitter.com/tedneward/statuses/1292396349"/> < link type="image/png" rel="image" href="http://s3.amazonaws.com/twitter_production/profile_images/ 55857457/javapolis_normal.png"/> < author> < name>Ted Neward< /name> < uri>http://www.tedneward.com< /uri> < /author> < /entry> < /feed>
狀態(tài)消息中的大部分元素(如果不是全部的話)都很直觀,因此不再贅述。
由于我們可以采用三種基于 XML 的格式使用 Twitter 消息,以及 Scala 具備一些非常強(qiáng)大的 XML 特性,包括 XML 字面值和類似 XPath 的查詢語法 API,因此編寫可以發(fā)送和接收 Twitter 消息的 Scala 庫只需要一些基礎(chǔ)的 Scala 編碼工作。舉例來說,通過 Scala 使用清單 1 消息來提取狀態(tài)更新的標(biāo)題或內(nèi)容可以利用 Scala 的 XML 類型和 \ 及 \\ 方法,如 清單 2 所示:
清單 2. 您好 Ted,您在哪里?
< ![CDATA[ package com.tedneward.scitter.test { class ScitterTest { import org.junit._, Assert._ @Test def simpleAtomParse = { val atom = < feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"> < title>Twitter / tedneward< /title> < id>tag:twitter.com,2007:Status< /id> < link type="text/html" rel="alternate" href="http://twitter.com/tedneward"/> < updated>2009-03-07T13:48:31+00:00< /updated> < subtitle>Twitter updates from Ted Neward / tedneward.< /subtitle> < entry> < title>tedneward: @kdellison Happens to the best of us...< /title> < content type="html">tedneward: @kdellison Happens to the best of us...< /content> < id>tag:twitter.com,2007: http://twitter.com/tedneward/statuses/1292396349< /id> < published>2009-03-07T11:07:18+00:00< /published> < updated>2009-03-07T11:07:18+00:00< /updated> < link type="text/html" rel="alternate" href="http://twitter.com/tedneward/statuses/1292396349"/> < link type="image/png" rel="image" href="http://s3.amazonaws.com/twitter_production/profile_images/ 55857457/javapolis_normal.png"/> < author> < name>Ted Neward< /name> < uri>http://www.tedneward.com< /uri> < /author> < /entry> < /feed> assertEquals(atom \\ "entry" \ "title", "tedneward: @kdellison Happens to the best of us...") } } } ]]>
有關(guān) Scala 的 XML 支持的更多詳細(xì)信息,請(qǐng)參閱 “Scala 和 XML”。
實(shí)際上,使用原始 XML 本身并不是一個(gè)有趣的練習(xí)。如果 Scala 的宗旨是讓我們的生活更加輕松,那么可以創(chuàng)建一個(gè)或一組專用于簡化 Scala 消息發(fā)送和接收任務(wù)的類。作為其中一個(gè)目標(biāo),應(yīng)該能夠在 “普通” Java 程序中方便地使用庫(這意味著可以方便地從任何可理解普通 Java 語義的環(huán)境中來訪問它,比如說 Groovy 或 Clojure)。
API 設(shè)計(jì)
在深入了解 Scala/Twitter 庫的 API 設(shè)計(jì)之前(根據(jù)同事 ThoughtWorker Neal Ford 的建議,我將它稱作 “Scitter”),需要明確一些需求。
首先,Scitter 顯然會(huì)對(duì)網(wǎng)絡(luò)訪問有一些依賴 — 并且可擴(kuò)展到 Twitter 服務(wù)器 — 這會(huì)使測(cè)試變得非常困難。
其次,我們需要解析(和測(cè)試)Twitter 發(fā)回的各種格式。
第三,我們希望隱藏 API 內(nèi)部各種格式之間的差異,以便客戶機(jī)不需要擔(dān)心已記錄的 Twitter 消息格式,但是可以僅使用標(biāo)準(zhǔn)類。
最后,由于 Twitter 依賴 “通過身份驗(yàn)證的用戶” 才能使用大量 API,因此 Scitter 庫需要適應(yīng) “驗(yàn)證” 和 “未驗(yàn)證” API 之間的差異,而不會(huì)讓事情變得過于復(fù)雜。
網(wǎng)絡(luò)訪問需要一些形式的 HTTP 通信,以便聯(lián)系 Twitter 服務(wù)器。雖然我們可以使用 Java 庫本身(特別是 URL 類及其同胞),但由于 Twitter API 需要大量請(qǐng)求和響應(yīng)主體連接,因此可以更加輕松地使用不同的 HTTP API,特別是 Apache Commons HttpClient 庫。為了更便于測(cè)試客戶機(jī) API,實(shí)際通信將隱藏在一些 API 內(nèi)部的 Scitter 庫中,以便能夠更加輕松地切換到另一個(gè) HTTP 庫(其必要性不太容易想到),并能模擬實(shí)際網(wǎng)絡(luò)通信以簡化測(cè)試(其作用很容易想到)。
結(jié)果,第一個(gè)測(cè)試是 Scala 化 HttpClient 調(diào)用,以確?;就ㄐ拍J骄臀?;注意,由于 HttpClient 依賴另外兩個(gè) Apache 庫(Commons Logging 和 Commons Codec),因此還需要在運(yùn)行時(shí)提供這些庫;對(duì)于那些希望開發(fā)相似種類代碼的讀者,確保類路徑中包括所有三個(gè)庫。
由于最易于使用的 Twitter API 是測(cè)試 API
因此在請(qǐng)求格式中返回 “ok”,并附帶 200 OK HTTP 狀態(tài)碼。
我們將使用它作為 Scitter 測(cè)試中的保留條款。它位于 URL http://twitter.com/help/test.format(其中,“format” 為 “xml” 或 “json”;至于目前,我們將選擇使用 “xml”),并且僅有的支持 HTTP 方法是 GET。HttpClient 代碼簡明易懂,如清單 3 所示:
清單 3. Twitter PING!
package com.tedneward.scitter.test { class ExplorationTests { // ... import org.apache.commons.httpclient._, methods._, params._, cookie._ @Test def callTwitterTest = { val testURL = "http://twitter.com/help/test.xml" // HttpClient API 101 val client = new HttpClient() val method = new GetMethod(testURL) method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() assertEquals(statusLine.getStatusCode(), 200) assertEquals(statusLine.getReasonPhrase(), "OK") } } }
此代碼中最重要的一部分是 HttpClient 樣板 — 感興趣的讀者應(yīng)該查閱 HttpClient API 文檔了解詳細(xì)信息。假設(shè)連接到公共 Internet 的網(wǎng)絡(luò)可用(并且 Twitter 并未修改其公共 API),那么該測(cè)試應(yīng)該能順利通過。
鑒于此,我們?cè)敿?xì)分析 Scitter 客戶機(jī)的第一部分。這意味著我們需要解決一個(gè)設(shè)計(jì)問題:如何構(gòu)建 Scitter 客戶機(jī)來處理經(jīng)過驗(yàn)證和未經(jīng)過驗(yàn)證的調(diào)用。目前,我將采用典型的 Scala 方式,假定驗(yàn)證是 “按對(duì)象” 執(zhí)行的,因此將需要驗(yàn)證的調(diào)用放在類定義中,并在未驗(yàn)證的調(diào)用放在對(duì)象定義中:
清單 4. Scitter.test
package com.tedneward.scitter { /** * Object for consuming "non-specific" Twitter feeds, such as the public timeline. * Use this to do non-authenticated requests of Twitter feeds. */ object Scitter { import org.apache.commons.httpclient._, methods._, params._, cookie._ /** * Ping the server to see if it's up and running. * * Twitter docs say: * test * Returns the string "ok" in the requested format with a 200 OK HTTP status code. * URL: http://twitter.com/help/test.format * Formats: xml, json * Method(s): GET */ def test : Boolean = { val client = new HttpClient() val method = new GetMethod("http://twitter.com/help/test.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() statusLine.getStatusCode() == 200 } } /** * Class for consuming "authenticated user" Twitter APIs. Each instance is * thus "tied" to a particular authenticated user on Twitter, and will * behave accordingly (according to the Twitter API documentation). */ class Scitter(username : String, password : String) { } }
目前,我們將網(wǎng)絡(luò)抽象放在一邊 — 稍后,當(dāng)離線測(cè)試變得更加重要時(shí)再添加它。當(dāng)我們更好地理解如何使用 HttpClient 類時(shí),這還將幫助避免 “過度抽象” 網(wǎng)絡(luò)通信。
由于已經(jīng)明確區(qū)分了驗(yàn)證和未驗(yàn)證 Twitter 客戶機(jī),因此我們將快速創(chuàng)建一個(gè)經(jīng)過驗(yàn)證的方法??瓷先?Twitter 提供了一個(gè)可驗(yàn)證用戶登錄憑證的 API。再次,HttpClient 代碼將類似于之前的代碼,除了將用戶名和密碼傳遞到 Twitter API 中之外。
這引出了 Twitter 如何驗(yàn)證用戶的概念??焖俨榭?Twitter API 頁面后,可以發(fā)現(xiàn) Twitter 使用的是一種 Stock HTTP 驗(yàn)證方法,這與任何經(jīng)過驗(yàn)證的資源在 HTTP 中的方法相同。這意味著 HttpClient 代碼必須提供用戶名和密碼作為 HTTP 請(qǐng)求的一部分,而不是作為 POST 的主體,如清單 5 所示:
清單 5. 您好 Twitter,是我!
package com.tedneward.scitter.test { class ExplorationTests { def testUser = "TwitterUser" def testPassword = "TwitterPassword" @Test def verifyCreds = { val client = new HttpClient() val verifyCredsURL = "http://twitter.com/account/verify_credentials.xml" val method = new GetMethod(verifyCredsURL) method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.getParams().setAuthenticationPreemptive(true) val defaultcreds = new UsernamePasswordCredentials(testUser, testPassword) client.getState().setCredentials(new AuthScope("twitter.com", 80, AuthScope.ANY_REALM), defaultcreds) client.executeMethod(method) val statusLine = method.getStatusLine() assertEquals(200, statusLine.getStatusCode()) assertEquals("OK", statusLine.getReasonPhrase()) } } }
注意,要讓此測(cè)試順利通信,用戶名和密碼字段將需要輸入 Twitter 能接收的內(nèi)容 — 我在開發(fā)時(shí)使用了自己的 Twitter 用戶名和密碼,但顯然您需要使用自己設(shè)定的用戶名和密碼。注冊(cè)新的 Twitter 帳戶相當(dāng)簡單,因此我假定您已經(jīng)擁有一個(gè)帳戶,或者知道如何注冊(cè)(很好。我會(huì)等待完成此任務(wù))。
完成后,使用用戶名和密碼構(gòu)造函數(shù)參數(shù)將它映射到 Scitter 類非常簡單,如清單 6 所示:
清單 6. Scitter.verifyCredentials
package com.tedneward.scitter { import org.apache.commons.httpclient._, auth._, methods._, params._ // ... /** * Class for consuming "authenticated user" Twitter APIs. Each instance is * thus "tied" to a particular authenticated user on Twitter, and will * behave accordingly (according to the Twitter API documentation). */ class Scitter(username : String, password : String) { /** * Verify the user credentials against Twitter. * * Twitter docs say: * verify_credentials * Returns an HTTP 200 OK response code and a representation of the * requesting user if authentication was successful; returns a 401 status * code and an error message if not. Use this method to test if supplied * user credentials are valid. * URL: http://twitter.com/account/verify_credentials.format * Formats: xml, json * Method(s): GET */ def verifyCredentials : Boolean = { val client = new HttpClient() val method = new GetMethod("http://twitter.com/help/test.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.getParams().setAuthenticationPreemptive(true) val creds = new UsernamePasswordCredentials(username, password) client.getState().setCredentials( new AuthScope("twitter.com", 80, AuthScope.ANY_REALM), creds) client.executeMethod(method) val statusLine = method.getStatusLine() statusLine.getStatusCode() == 200 } } }
清單 7 中相應(yīng)的 Scitter 類測(cè)試也相當(dāng)簡單:
清單 7. 測(cè)試 Scitter.verifyCredentials
package com.tedneward.scitter.test { class ScitterTests { import org.junit._, Assert._ import com.tedneward.scitter._ def testUser = "TwitterUsername" def testPassword = "TwitterPassword" // ... @Test def verifyCreds = { val scitter = new Scitter(testUser, testPassword) val result = scitter.verifyCredentials assertTrue(result) } } }
不算太糟。庫的基本結(jié)構(gòu)已經(jīng)成形,但顯然還有很長的路要走,特別是因?yàn)槟壳皩?shí)際上未執(zhí)行任何特定于 Scala 的任務(wù) — 在面向?qū)ο笤O(shè)計(jì)中,庫的構(gòu)建并不像練習(xí)那樣簡單。因此,我們開始使用一些 XML,并通過更加合理的格式將它返回。
從 XML 到對(duì)象
現(xiàn)在可以添加的最簡單的 API 是 public_timeline,它收集 Twitter 從所有用戶處接收到的最新的 n 更新,并返回它們以便于進(jìn)行使用。與之前討論的另外兩個(gè) API 不同,public_timeline API 返回一個(gè)響應(yīng)主體(而不是僅依賴于狀態(tài)碼),因此我們需要分解生成的 XML/RSS/ATOM/,然后將它們返回給 Scitter 客戶機(jī)。
現(xiàn)在,我們編寫一個(gè)探索測(cè)試,它將訪問公共提要并將結(jié)果轉(zhuǎn)儲(chǔ)到 stdout 以便進(jìn)行分析,如清單 8 所示:
清單 8. 大家都在忙什么?
package com.tedneward.scitter.test { class ExplorationTests { // ... @Test def callTwitterPublicTimeline = { val publicFeedURL = "http://twitter.com/statuses/public_timeline.xml" // HttpClient API 101 val client = new HttpClient() val method = new GetMethod(publicFeedURL) method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() assertEquals(statusLine.getStatusCode(), 200) assertEquals(statusLine.getReasonPhrase(), "OK") val responseBody = method.getResponseBodyAsString() System.out.println("callTwitterPublicTimeline got... ") System.out.println(responseBody) } } }
運(yùn)行后,結(jié)果每次都會(huì)有所不同,因?yàn)楣?Twitter 服務(wù)器上有許多用戶,但通常應(yīng)與清單 9 的 JUnit 文本文件轉(zhuǎn)儲(chǔ)類似:
清單 9. 我們的 Tweets 結(jié)果
< statuses type="array"> < status> < created_at>Tue Mar 10 03:14:54 +0000 2009< /created_at> < id>1303777336< /id> < text>She really is. http://tinyurl.com/d65hmj< /text> < source>< a href="http://iconfactory.com/software/twitterrific">twitterrific< /a> < /source> < truncated>false< /truncated> < in_reply_to_status_id>< /in_reply_to_status_id> < in_reply_to_user_id>< /in_reply_to_user_id> < favorited>false< /favorited> < user> < id>18729101< /id> < name>Brittanie< /name> < screen_name>brittaniemarie< /screen_name> < description>I'm a bright character. I suppose.< /description> < location>Atlanta or Philly.< /location> < profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/ 81636505/goodish_normal.jpg< /profile_image_url> < url>http://writeitdowntakeapicture.blogspot.com< /url> < protected>false< /protected> < followers_count>61< /followers_count> < /user> < /status> < status> < created_at>Tue Mar 10 03:14:57 +0000 2009< /created_at> < id>1303777334< /id> < text>Number 2 of my four life principles. "Life is fun and rewarding"< /text> < source>web< /source> < truncated>false< /truncated> < in_reply_to_status_id>< /in_reply_to_status_id> < in_reply_to_user_id>< /in_reply_to_user_id> < favorited>false< /favorited> < user> < id>21465465< /id> < name>Dale Greenwood< /name> < screen_name>Greeendale< /screen_name> < description>Vegetarian. Eat and use only organics. Love helping people become prosperous< /description> < location>Melbourne Australia< /location> < profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/ 90659576/Dock_normal.jpg< /profile_image_url> < url>http://www.4abundance.mionegroup.com< /url> < protected>false< /protected> < followers_count>15< /followers_count> < /user> < /status> (A lot more have been snipped) < /statuses>
通過查看結(jié)果和 Twitter 文檔可以看出,調(diào)用的結(jié)果是一組具備一致消息結(jié)構(gòu)的簡單 “狀態(tài)” 消息。使用 Scala 的 XML 支持分離結(jié)果相當(dāng)簡單,但我們會(huì)在基本測(cè)試通過后立即簡化它們,如清單 10 所示:
清單 10. 大家都在忙什么?
package com.tedneward.scitter.test { class ExplorationTests { // ... @Test def simplePublicFeedPullAndParse = { val publicFeedURL = "http://twitter.com/statuses/public_timeline.xml" // HttpClient API 101 val client = new HttpClient() val method = new GetMethod(publicFeedURL) method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) val statusCode = client.executeMethod(method) val responseBody = new String(method.getResponseBody()) val responseXML = scala.xml.XML.loadString(responseBody) val statuses = responseXML \\ "status" for (n < - statuses.elements) { n match { case < status>{ contents @ _*}< /status> => { System.out.println("Status: ") contents.foreach((c) => c match { case < text>{ t @ _*}< /text> => System.out.println("\tText: " + t.text.trim) case < user>{ contents2 @ _* }< /user> => { contents2.foreach((c2) => c2 match { case < screen_name>{ u }< /screen_name> => System.out.println("\tUser: " + u.text.trim) case _ => () } ) } case _ => () } ) } case _ => () // or, if you prefer, System.out.println("Unrecognized element!") } } } } }
隨著示例代碼模式的變化,這并不值得推薦 — 這有點(diǎn)類似于 DOM,依次導(dǎo)航到各個(gè)子元素,提取文本,然后導(dǎo)航到另一個(gè)節(jié)點(diǎn)。我可以僅執(zhí)行兩個(gè) XPath 樣式的查詢,如清單 11 所示:
清單 11. 替代解析方法
for (n < - statuses.elements) { val text = (n \\ "text").text val screenName = (n \\ "user" \ "screen_name").text }
這顯然更加簡短,但它帶來了兩個(gè)基本問題:
我們可以強(qiáng)制 Scala 的 XML 庫針對(duì)每個(gè)元素或子元素遍歷一次圖,其速度會(huì)隨時(shí)間減慢。
我們?nèi)匀恍枰苯犹幚?XML 消息的結(jié)構(gòu)。這是兩個(gè)問題中最為重要的。
也就是說,這種方式不具備可伸縮性 — 假設(shè)我們最終對(duì) Twitter 狀態(tài)消息中的每個(gè)元素都感興趣,我們將需要分別從各狀態(tài)中提取各個(gè)元素。
這又造成了另一個(gè)與各格式本身相關(guān)的問題。記住,Twitter 可以使用四種不同的格式,并且我們不希望 Scitter 客戶機(jī)需要了解它們之間的任何差異,因此 Scitter 需要一個(gè)能返回給客戶機(jī)的中間結(jié)構(gòu),以便未來使用,如清單 12 所示:
清單 12. Breaker,您的狀態(tài)是什么?
abstract class Status { val createdAt : String val id : Long val text : String val source : String val truncated : Boolean val inReplyToStatusId : Option[Long] val inReplyToUserId : Option[Long] val favorited : Boolean val user : User }
這與 User 方式相類似,考慮到簡潔性,我就不再重復(fù)了。注意,User 子元素有一個(gè)有趣的問題 — 雖然存在 Twitter 用戶類型,但其中內(nèi)嵌了一個(gè)可選的 “最新狀態(tài)”。狀態(tài)消息還內(nèi)嵌了一個(gè)用戶。對(duì)于這種情況,為了幫助避免一些潛在的遞歸問題,我選擇創(chuàng)建一個(gè)嵌入在 Status 內(nèi)部的 User 類型,以反映所出現(xiàn)的 User 數(shù)據(jù);反之亦然,Status 也可以嵌入在 User 中,這樣可以明確避免該問題。(至少,在沒發(fā)現(xiàn)問題之前,這種方法是有效的)。
現(xiàn)在,創(chuàng)建了表示 Twitter 消息的對(duì)象類型之后,我們可以遵循 XML 反序列化的公共 Scala 模式:創(chuàng)建相應(yīng)的對(duì)象定義,其中包含一個(gè) fromXml 方法,用于將 XML 節(jié)點(diǎn)分離到對(duì)象實(shí)例中,如清單 13 所示:
清單 13. 分解 XML
/** * Object wrapper for transforming (format) into Status instances. */ object Status { def fromXml(node : scala.xml.Node) : Status = { new Status { val createdAt = (node \ "created_at").text val id = (node \ "id").text.toLong val text = (node \ "text").text val source = (node \ "source").text val truncated = (node \ "truncated").text.toBoolean val inReplyToStatusId = if ((node \ "in_reply_to_status_id").text != "") Some((node \"in_reply_to_status_id").text.toLong) else None val inReplyToUserId = if ((node \ "in_reply_to_user_id").text != "") Some((node \"in_reply_to_user_id").text.toLong) else None val favorited = (node \ "favorited").text.toBoolean val user = User.fromXml((node \ "user")(0)) } } }
其中最強(qiáng)大的一處是,它可以針對(duì) Twitter 支持的其他任何格式進(jìn)行擴(kuò)展 — fromXml 方法可以在分解節(jié)點(diǎn)之前檢查它是否保存了 XML、RSS 或 Atom 類型的內(nèi)容,或者 Status 可以包含 fromXml、fromRss、fromAtom 和 fromJson 方法。實(shí)際上,后一種方法是我的優(yōu)先選擇,因?yàn)樗鼤?huì)平等對(duì)待基于 XML 的格式和 JSON(基于文本)格式。
好奇和細(xì)心的讀者會(huì)注意到在 Status 及其內(nèi)嵌 User 的 fromXml 方法中,我使用的是 XPath 樣式的分解方法,而不是之前建議的遍歷內(nèi)嵌元素的方法?,F(xiàn)在,XPath 樣式的方法看上去更易于閱讀,但幸運(yùn)的是,我后來改變了注意,良好的封裝仍然是我的朋友 — 我可以在隨后修改它,Scitter 外部的任何人都不會(huì)知道。
注意 Status 內(nèi)部的兩個(gè)成員如何使用 Option[T] 類型;這是因?yàn)檫@些元素通常排除在 Status 消息外部,并且雖然元素本身會(huì)出現(xiàn),但它們顯示為空(類似于 < in_reply_to_user_id>< /in_reply_to_user_id>)。這正是 Option[T] 的作用所在。當(dāng)元素為空時(shí),它們將使用 “None” 值。(這表示考慮到基于 Java 的兼容性,訪問它們會(huì)更加困難,但惟一可行方法是對(duì)最終生成的 Option 實(shí)例調(diào)用 get(),這不太復(fù)雜并且能很好地解決 “非 null 即 0” 問題)。
現(xiàn)在已經(jīng)可以輕而易舉地使用公共時(shí)間軸:
清單 14. 分解公共時(shí)間軸
@Test def simplePublicFeedPullAndDeserialize = { val publicFeedURL = "http://twitter.com/statuses/public_timeline.xml" // HttpClient API 101 val client = new HttpClient() val method = new GetMethod(publicFeedURL) method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) val statusCode = client.executeMethod(method) val responseBody = new String(method.getResponseBody()) val responseXML = scala.xml.XML.loadString(responseBody) val statuses = responseXML \\ "status" for (n < - statuses.elements) { val s = Status.fromXml(n) System.out.println("\t'@" + s.user.screenName + "' wrote " + s.text) } }
顯然,這看上去更加簡潔,并且易于使用。
將所有這些結(jié)合到 Scitter 單一實(shí)例中相當(dāng)簡單,僅涉及執(zhí)行查詢、解析各個(gè) Status 元素以及將它們添加到 List[Status] 實(shí)例中,如清單 15 所示:
清單 15. Scitter.publicTimeline
package com.tedneward.scitter { import org.apache.commons.httpclient._, auth._, methods._, params._ import scala.xml._ object Scitter { // ... /** * Query the public timeline for the most recent statuses. * * Twitter docs say: * public_timeline * Returns the 20 most recent statuses from non-protected users who have set * a custom user icon. Does not require authentication. Note that the * public timeline is cached for 60 seconds so requesting it more often than * that is a waste of resources. * URL: http://twitter.com/statuses/public_timeline.format * Formats: xml, json, rss, atom * Method(s): GET * API limit: Not applicable * Returns: list of status elements */ def publicTimeline : List[Status] = { import scala.collection.mutable.ListBuffer val client = new HttpClient() val method = new GetMethod("http://twitter.com/statuses/public_timeline.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() if (statusLine.getStatusCode() == 200) { val responseXML = XML.loadString(method.getResponseBodyAsString()) val statusListBuffer = new ListBuffer[Status] for (n < - (responseXML \\ "status").elements) statusListBuffer += (Status.fromXml(n)) statusListBuffer.toList } else { Nil } } } }
在實(shí)現(xiàn)功能全面的 Twiter 客戶機(jī)之前,我們顯然還有很長的路要走。但到目前為止,我們已經(jīng)實(shí)現(xiàn)基本的行為。
感謝各位的閱讀,以上就是“Twitter是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Twitter是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
文章名稱:Twitter是什么
鏈接分享:http://aaarwkj.com/article10/gihpdo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁設(shè)計(jì)公司、網(wǎng)站維護(hù)、網(wǎng)站設(shè)計(jì)、電子商務(wù)、軟件開發(fā)、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)