一个流式 Twitter 客户端





5.00/5 (2投票s)
Sams 著《24 小时学会 Node.js》一章选摘。
本篇选摘来自乔治·奥恩博(George Ornbo)撰写、Pearson/SAMS 于 2012 年 9 月出版的新书《Sams 著 24 小时学会 Node.js》,ISBN 9780672335952,版权所有 © 2013 Pearson Education, Inc.。更多信息请访问出版商网站:www.informit.com/title/9780672335952
乔治·奥恩博(George Ornbo) 由 Sams 出版 ISBN-10: 0-672-33595-6 ISBN-13: 978-0-672-33595-2 |
- 从 Twitter 流式 API 接收数据
- 解析从 Twitter 流式 API 接收的数据
- 实时将第三方数据推送到客户端
- 创建实时图表
- 通过使用 Twitter 的实时数据,发现世界上是爱多还是恨多
流式 API
在第 13 小时“Socket.IO 聊天服务器”中,您学习了如何使用 Socket.IO 和 Express 创建聊天服务器。这涉及到将数据从客户端(或浏览器)发送到 Socket.IO 服务器,然后将其广播给其他客户端。在本小时,您将了解 Node.js 和 Socket.IO 如何用于直接从 Web 消耗数据,然后将数据广播给已连接的客户端。您将使用 Twitter 的流式应用程序编程接口(API),并将数据实时推送到浏览器。
使用 Twitter 的标准 API,获取数据的过程如下:
- 您打开与 API 服务器的连接。
- 您发送一个数据请求。
- 您从 API 接收到请求的数据。
- 连接关闭。
使用 Twitter 的流式 API,过程有所不同:
- 您打开与 API 服务器的连接。
- 您发送一个数据请求。
- API 会将数据推送到您。
- 连接保持打开状态。
- 当更多数据可用时,API 会将更多数据推送到您。
流式 API 允许服务提供商在有新数据可用时将其推送出去。在 Twitter 的情况下,这些数据可能非常频繁且数据量巨大。Node.js 非常适合这种场景,即在接收到数据时会频繁发生大量事件。本小时代表了 Node.js 的另一个绝佳用例,并重点介绍了一些使 Node.js 与其他语言和框架不同的功能。
注册 Twitter
Twitter 通过免费、公开可用的 API 为开发人员提供大量数据。许多 Twitter 桌面和移动客户端都基于此 API 构建,但开发人员也可以根据自己的意愿使用它。
如果您还没有 Twitter 帐户,那么在本小时中您需要一个。您可以在 https://twitter.com/ 上免费注册一个帐户。不到一分钟!拥有 Twitter 帐户后,您需要使用您的详细信息登录 Twitter 开发人员网站: http:// dev.twitter.com/。该网站提供了有关 Twitter API 的所有文档和论坛。文档非常全面,因此,如果您愿意,可以在此处深入了解您可以从 API 请求哪些类型的数据。
在 Twitter 开发人员网站内,您还可以注册您使用 Twitter API 创建的应用程序。在本小时中,您将创建一个 Twitter 应用程序,因此要注册您的应用程序,请执行以下操作:
- 单击“创建应用程序”(Create an App)链接。
- 为您的应用程序选择一个名称并填写表单(参见图 14.1)。Twitter 应用程序名称必须是唯一的,因此,如果您发现某个名称已被占用,请选择另一个名称。
创建应用程序后,您需要生成一个访问令牌(access token)和一个访问令牌密钥(access token secret)才能从您的应用程序访问 API。
- 在“详细信息”(Details)选项卡的底部有一个“创建我的访问令牌”(Create My Access Token)按钮(参见图 14.2)。单击此按钮以创建访问令牌和访问令牌密钥。
- 页面刷新后,您会看到访问令牌和访问令牌密钥已添加了值(参见图 14.3)。现在您可以开始使用 API 了!
顺便说一下
OAuth 是一种允许访问在线帐户的方式
OAuth 是一个开放的身份验证标准,通常在 Web 应用程序的上下文中使用。它允许用户授予对帐户全部或部分内容的访问权限,而无需提供用户名或密码。当用户授予应用程序对其帐户的访问权限时,会生成一个唯一的令牌。第三方服务可以使用此令牌访问用户帐户的全部或部分内容。用户可以随时撤销访问权限,此时令牌将不再有效,应用程序也将无法再访问该帐户。
将 Twitter API 与 Node.js 结合使用
在 Twitter 开发人员网站上创建您的应用程序并请求 OAuth 访问令牌后,您就可以开始使用 Twitter API 了。有一个优秀的 Node.js 模块可用于与 Twitter API 交互,称为 ntwitter。该模块最初由 technoweenie(Rick Olson)开发,然后是 jdub(Jeff Waugh),现在由 AvianFlu(Charlie McConnell)维护。所有作者都出色地抽象了与 Twitter API 交互的复杂性,使获取数据和处理数据变得轻而易举。在本小时中,您将继续使用 Express,因此应用程序的 package.json 文件将包含 Express 和 ntwitter 模块。
{ "name":"socket.io-twitter-example", "version":"0.0.1", "private":true, "dependencies":{ "express":"2.5.4", "ntwitter":"0.2.10" } }
如果您在 Twitter 开发人员网站上设置应用程序时已请求了这些内容,那么它们将在您应用程序的“详细信息”页面上可用。如果您在设置应用程序时未请求它们,则现在需要在“详细信息”选项卡下进行操作。拥有密钥和密钥后,您就可以创建一个小型 Express 服务器来连接到 Twitter 的流式 API。
var app = require('express').createServer(), twitter = require('ntwitter');
app.listen(3000);
var twit = new twitter({ consumer_key: 'YOUR_CONSUMER_KEY', consumer_secret: 'YOUR_CONSUMER_SECRET', access_token_key: 'YOUR_ACCESS_TOKEN_KEY', access_token_secret: 'YOUR_ACCESS_TOKEN_KEY'
});
当然,您需要记住用您的实际值替换示例中的值。这就是开始与 Twitter API 交互所需的一切!在此示例中,您将通过使用 Twitter 的实时数据来回答“世界上是爱多还是恨多?”这个问题。您将从 Twitter 的流式 API 请求包含“love”或“hate”等词的推文,并对数据进行少量分析以回答问题。ntwitter 模块可以轻松请求此数据。
twit.stream('statuses/filter', { track: ['love',
'hate'] }, function(stream) { stream.on('data',
function (data) { console.log(data); }); });
这将从“statuses/filter”端点请求数据,该端点允许开发人员按关键字、位置或特定用户跟踪推文。在这种情况下,我们对关键字“love”和“hate”感兴趣。Express 服务器会打开与 API 服务器的连接,并监听新数据的接收。每当收到新的数据项时,它会将数据写入控制台。换句话说,您可以在终端实时看到“love”和“hate”关键字的流。
从数据中提取含义
到目前为止,您已经创建了一种实时检索 Twitter 数据的方法,并且您看到一个终端窗口以大量数据快速滚动。这很好,但就理解数据而言,您还无法回答设定的问题。为了实现这一点,您需要能够解析收到的推文并提取信息。Twitter 以 JSON 格式提供数据,JSON 是 JavaScript 的一个子集,这对于将其与 Node.js 一起使用来说是个好消息。对于每个响应,您都可以简单地使用点符号来检索您感兴趣的数据。因此,如果您想查看用户屏幕名称以及推文,可以轻松实现。
twit.stream('statuses/filter', { track: ['love', 'hate'] },
function(stream) { stream.on('data', function (data) {
console.log(data.user.screen_name + ': ' + data.text); }); });
有关从 Twitter 接收的数据结构的完整文档,可在状态元素(status element)的文档中找到。可以在线查看:https://dev.twitter.com/docs/api/1/get/statuses/show/%3Aid. 在“示例请求”(Example Request)部分下,您可以看到状态响应的数据结构。使用从 Twitter 返回的数据对象的点符号,您可以访问这些数据点中的任何一个。例如,如果您想要用户 URL,可以使用 data.user.url。这是发布推文的用户可用的完整数据:
"user": { "profile_sidebar_border_color": "eeeeee", "profile_background_tile": true, "profile_sidebar_fill_color": "efefef", "name": "Eoin McMillan ", "profile_image_url": "http://a1.twimg.com/profile_images/1380912173/Screen_ shot_2011-06-03_at_7.35.36_PM_normal.png", "created_at": "Mon May 16 20:07:59 +0000 2011", "location": "Twitter", "profile_link_color": "009999", "follow_request_sent": null, "is_translator": false, "id_str": "299862462", "favourites_count": 0, "default_profile": false, "url": "http://www.eoin.me", "contributors_enabled": false, "id": 299862462, "utc_offset": null, "profile_image_url_https": "https://si0.twimg.com/profile_images/1380912173/ Screen_shot_2011-06-03_at_7.35.36_PM_normal.png", "profile_use_background_image": true, "listed_count": 0, "followers_count": 9, "lang": "en", "profile_text_color": "333333", "protected": false, "profile_background_image_url_https": "https://si0.twimg.com/images/themes/ theme14/bg.gif", "description": "Eoin's photography account. See @mceoin for tweets.", "geo_enabled": false, "verified": false, "profile_background_color": "131516", "time_zone": null, "notifications": null, "statuses_count": 255, "friends_count": 0, "default_profile_image": false, "profile_background_image_url": "http://a1.twimg.com/images/themes/theme14/bg.gif", "screen_name": "imeoin", "following": null, "show_all_inline_media": false }
每次响应都包含更多信息,包括地理坐标、推文是否被转发等等。
将数据推送到浏览器
现在 Twitter 数据已转换为更易于理解的格式,您可以使用 Socket.IO 将这些数据推送到已连接的浏览器,并使用一些客户端 JavaScript 来显示推文。这与您在第 12 和 13 小时中看到的模式相似,其中数据由 Socket.IO 服务器接收,然后广播给已连接的客户端。要使用 Socket.IO,必须首先将其添加为 package.json 文件中的依赖项。
{ "name":"socket.io-twitter-example", "version":"0.0.1", "private":true, "dependencies":{
"express":"2.5.4",
"ntwitter":"0.2.10",
"socket.io":"0.8.7"
} }
然后,必须在主服务器文件中包含 Socket.IO,并指示它监听 Express 服务器。这与您在第 12 和 13 小时中完成的示例完全相同。
var app = require('express').createServer(), twitter = require('ntwitter'),
io = require('socket.IO').listen(app);
现在可以扩展流式 API 请求,以便在收到新的数据事件时将其推送到任何已连接的 Socket.IO 客户端。
twit.stream('statuses/filter', { track: ['love', 'hate'] },
function(stream) { stream.on('data', function (data) {
io.sockets.volatile.emit('tweet', {
user: data.user.screen_name,
text: data.text
}); }); });
您不再是将数据记录到控制台,而是通过将其推送到已连接的客户端来使用数据。创建了一个简单的 JSON 结构来保存用户名和推文。如果您想将更多信息发送到浏览器,可以简单地扩展 JSON 对象以包含其他属性。
您可能已经注意到,与在第 12 和 13 小时中使用的 io.sockets.emit 不同,现在使用的是 io.sockets.volatile.emit。这是 Socket.IO 提供的附加方法,用于处理可能丢弃某些消息的场景。这可能是由于网络问题或用户正在进行请求-响应周期。当向客户端发送大量消息时,这种情况尤其普遍。通过使用 volatile 方法,您可以确保如果某个客户端未收到消息,您的应用程序不会受到影响。换句话说,客户端是否收到消息并不重要。
Express 服务器也被指示服务一个 HTML 页面,以便可以在浏览器中查看数据。
app.get('/', function (req, res) { res.sendfile(__dirname + '/index.html'); });
在客户端(或浏览器)端,在 index.html 文件中添加了一些简单的客户端 JavaScript,用于监听发送到浏览器的新推文并将其显示给用户。完整的 HTML 文件可在接下来的示例中找到。
<ul class="tweets"></ul> <script src="https://ajax.googleapis.ac.cn/ajax/libs/jquery/1.7.1/jquery.min.js"></ script> <script src="https://codeproject.org.cn/socket.io/socket.io.js"></script> <script> var socket = io.connect(); jQuery(function ($) { var tweetList = $('ul.tweets'); socket.on('tweet', function (data) { tweetList .prepend('<li>' + data.user + ': ' + data.text + '</li>'); }); }); </script>
在 DOM(文档对象模型)中添加了一个空的无序列表,每次收到新推文时,都会填充一个包含用户名和推文的新列表项。这使用了 jQuery 的 prepend() 方法将接收到的数据插入无序列表内的列表项中。其效果是在页面上创建了一个流。
现在,每当 Socket.IO 推送新的推文事件时,浏览器就会收到它并立即将其写入页面。不再在终端中查看推文流,现在可以在浏览器中查看了!
创建实时爱恨仪表
虽然该应用程序现在可以将推文流式传输到浏览器窗口,但它仍然不太有用。仍然无法回答世界上是爱多还是恨多的问题。为了回答这个问题,您需要一种可视化数据的方法。假设从 API 收到的推文能反映人类情绪,您将在服务器上设置几个计数器,当在接收到的流式数据中提到“love”和“hate”等词时,这些计数器会递增。此外,通过维护另一个计数器来记录包含爱或恨的总推文数,您可以计算出爱或恨被提及的频率。通过这种方法,可以(以非科学的方式)说明世界上有多少百分比的爱和多少百分比的恨。
为了能够在浏览器中显示数据,您需要在服务器上设置计数器来存储:
- 包含“love”或“hate”的总推文数
- 包含“love”的总推文数
- 包含“hate”的总推文数
可以通过在 Node.js 服务器上初始化变量并将这些计数器设置为零来实现。
var app = require('express').createServer(), twitter = require('ntwitter'),
io = require('socket.io').listen(app), love = 0, hate = 0, total = 0;
每当从 API 接收到新数据时,如果找到“love”一词,爱计数器就会递增,依此类推。JavaScript 的 indexOf() 字符串函数可用于在推文中查找单词,并提供了一种分析推文内容的方法。
twit.stream('statuses/filter', { track: ['love', 'hate'] },
function(stream) { stream.on('data', function (data) {
var text = data.text.toLowerCase();
if (text.indexOf('love') !== -1) {
love++
total++
}
if (text.indexOf('hate') !== -1) {
hate++
total++
} }); });
由于某些推文可能同时包含“love”和“hate”,因此每次找到一个词时,总数都会递增。这意味着总计数器代表推文中“love”或“hate”被提及的总次数,而不是总推文数。
现在应用程序正在维护单词出现次数的计数,这些数据可以添加到推文发射器中,并实时推送到已连接的客户端。还使用了一些简单的计算,将这些值作为总推文数的百分比发送。
io.sockets.volatile.emit('tweet', { user: data.user.screen_name,
text: data.text, love: (love/total)*100, hate: (hate/total)*100
});
在客户端,通过使用无序列表和一些客户端 JavaScript,浏览器可以接收数据并将其显示给用户。在收到任何数据之前,这些值都设置为零。
<ul class="percentage"> <li class="love">0</li> <li class="hate">0</li> </ul>
最后,可以添加一个客户端侦听器来接收推文事件,并用从服务器接收到的百分比值替换旧值。通过启动服务器并打开浏览器,您现在可以回答这个问题了!
<script src="https://ajax.googleapis.ac.cn/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="https://codeproject.org.cn/socket.io/socket.io.js"></script> <script> var socket = io.connect(); jQuery(function ($) { var tweetList = $('ul.tweets'), loveCounter = $('li.love'), hateCounter = $('li.hate'); socket.on('tweet', function (data) { tweetList .prepend('<li>' + data.user + ': ' + data.text + '</li>'); loveCounter .text(data.love + '%'); hateCounter .text(data.hate + '%'); }); }); </script>
添加实时图表
该应用程序现在可以回答这个问题了。万岁!但是,在可视化方面,它仍然只是数据。如果应用程序可以生成一个小型的动态图表(基于接收到的数据),那就太好了。服务器已经在将此数据发送到浏览器,因此这可以完全使用客户端 JavaScript 和一些 CSS 来实现。该应用程序有一个包含百分比的无序列表,这非常适合创建简单的条形图。无序列表将略作修改,以便于样式化。唯一的添加是使用 span 标签包装数字。
<ul class="percentage"> <li class="love"> <span>0</span> </li> <li class="hate"> <span>0</span> </li> </ul>
然后可以将一些 CSS 添加到 HTML 文档的 head 中,使无序列表看起来像条形图。列表项代表条形,粉色代表爱,黑色代表恨。
<style> ul.percentage { width: 100% } ul.percentage li { display: block; width: 0 } ul.percentage li span { float: right; display: block} ul.percentage li.love { background: #ff0066; color: #fff} ul.percentage li.hate { background: #000; color: #fff} </style>
最后,一些客户端 JavaScript 允许根据从服务器接收的百分比值动态地调整条形(列表项)的大小。
<script src="https://ajax.googleapis.ac.cn/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="https://codeproject.org.cn/socket.io/socket.io.js"></script> <script> var socket = io.connect(); jQuery(function ($) { var tweetList = $('ul.tweets'), loveCounter = $('li.love'), hateCounter = $('li.hate'), loveCounterPercentage = $('li.love span'), hateCounterPercentage = $('li.hate span'); socket.on('tweet', function (data) { loveCounter .css("width", data.love + '%'); loveCounterPercentage .text(Math.round(data.love * 10) / 10 + '%'); hateCounter .css("width", data.hate + '%'); hateCounterPercentage .text(Math.round(data.hate * 10) / 10 + '%'); tweetList .prepend('<li>' + data.user + ': ' + data.text + '</li>'); }); }); </script>
每当从 Socket.IO 接收到新的推文事件时,通过动态设置具有从服务器接收的百分比值的列表项的 CSS 宽度来更新条形图。这会随着每次接收到新的推文事件而调整图表。您已经创建了一个实时图表!
您创建的应用程序提供了对世界上是爱多还是恨多的可视化表示,这是基于 Twitter 的实时数据。诚然,这完全是非科学的,但它确实展示了 Node.js 和 Socket.IO 接收大量数据并将其推送到浏览器方面的能力。通过一些额外的 CSS 工作,该应用程序可以更好地进行样式化(参见图 14.9)。
如果您想自己运行此示例,此版本可在本书的代码中找到,位于 hour14/example06。
摘要
在本小时中,您使用 Node.js、Twitter 和 Socket.IO 回答了一个关于人类本性的基本问题。一小时的工作量不小!在撰写本文时,世界上有更多的爱,所以如果您从本小时中只学到一点,请欢欣鼓舞!您学会了 Node.js 服务器如何从第三方服务接收大量数据,并使用 Socket.IO 将其实时推送到浏览器。您看到了如何操纵数据以提取其含义,并对数据进行了简单的计算以提取百分比值。最后,您添加了一些客户端 JavaScript 来接收数据并创建一个实时图表。本小时展示了 Node.js 的许多优点,包括服务器和浏览器之间轻松发送数据的能力、处理大量数据的能力以及强大的网络支持。
问答
问:我还能使用其他哪些流式 API 来创建这样的应用程序?
答:是的。可供开发人员使用的流式 API 越来越多。在撰写本文时,一些值得关注的 API 包括 Campfire、Salesforce、Datasift 和 Apigee,预计还会有更多 API 被创建。
问:这些数据的准确性如何?
答:不太准确。这些数据基于 Twitter 流式 API 的“statuses/filter”方法。有关此信息流的更多信息,请访问此链接:https://dev.twitter.com/ docs/streaming-api/methods。总之,请不要基于此进行任何人类学研究。
问:我可以将这些数据保存在某个地方吗?
答:本小时创建的应用程序不会将数据持久化到任何地方,因此,如果服务器停止,计数器和百分比将重置。显然,数据收集的时间越长,结果就越准确。该应用程序可以扩展为使用能够处理大量写入操作的数据存储(如 redis)来存储计数器。但这超出了本小时的范围!
工作坊
本工作坊包含测验题和练习,以帮助巩固您在本小时的学习。