五、推特
Twitter 是一个真正的成长于开放和可用的 API 之上的服务。 最初,没有 Twitter 客户端。 与 Twitter 的交流仅限于短信和后来的网站。 在开发 Twitter 的过程中,开发人员显然在短信费用、测试和构建系统方面积累了数百美元。 Twitter 的普及得益于数百名开发人员,他们使用开放的 Twitter api 或 Twitter RSS 源,构建了TweetDeck和Tweetree等工具。 因此,Twitter 提供了一个丰富的 API,可用于构建应用,在我们的例子中是可视化。
在开始构建可视化之前,让我们先看看 Twitter API,以及如何利用它。 在https://dev.twitter.com上有大量的 API 文档。 如果你想要更多的信息,这应该是你做进一步研究的第一站。
出于我们的目的,Twitter 提供了两种不同的模型来检索数据。 第一种是典型的 RESTful 模型,其中客户端向 Twitter 请求特定的资源,这些资源通过 HTTP 以 JSON 格式返回。 这个 API 可能与您以前见过的其他 API 类似。 它是无状态的,这意味着在请求之间不保留服务器端信息,并遵循 HTTP 的最佳实践。 如果您试图在 web 浏览器中使用来自 Twitter 的数据,那么这是您的选择。 第二个选项是流 API。 此方法使用一个持久的 HTTP 连接,当消息发生时,Twitter 将向该连接发送消息。 从浏览器中使用流 API 通常是一个糟糕的主意,所以你需要在浏览器和 API 之间有一个中介服务器,如下图所示:
不幸的是,拥有服务器是所有使用 Twitter(甚至是 RESTful API)的可视化的要求,因为 Twitter 不支持使用纯基于浏览器的解决方案进行身份验证。 我们稍后会详细介绍,但首先我们需要在 Twitter 上建立一个开发者账户。
访问 api
如果你还记得在第 3 章,OAuth中,其中一个要求是为我们想要交谈的每个站点获取一个应用键。 这也适用于 Twitter,让我们开始吧。
打开浏览器,转到https://dev.twitter.com,点击登录链接。 如果你已经有一个 Twitter 帐户,那么你可以在这里使用它来登录。 如果没有,你可以注册一个新的 Twitter 账户。 别担心,都是免费的。
一旦你登录,然后在右上角应该有一个链接到我的应用,如下截图:
点击它将带您进入一个页面,在那里您可以开始设置您的第一个应用。 你需要输入一些关于申请的信息。 您可以为大多数字段输入任何您选择的字段,但回调 URL 应该是http://127.0.0.1:8080/twitter1.html。 这是当 OAuth 阶段完成后 Twitter 会指向你的 URL。 我们在这里使用的是 localhost 值,但在生产中,您可能希望使用面向公共的 URL 进行可视化。 Application Details窗口的截图如下:
提示
这里不能使用本地主机域,但如果您不想看到 IP 地址,那么可以使用 URL 缩短服务为本地主机 URL 创建别名。 确保你的网址缩短保留查询参数,否则你将无法正确登录。
一旦你的应用已经被创建,你将能够看到从相同的我的应用选项我们以前使用过的各种设置。 我们需要的关键信息为OAuth settings,
,如下图所示:
这些密钥将用于我们的应用的用户授权。 如果秘密密钥泄露了——例如,你可能已经把它粘贴到你正在写的书中——它可以使用reset keys链接进行重置。 这样做可以防止你书中的读者冒充你,以你的名义犯下难以言喻的恶行。
设置服务器
正如我提到的,Twitter 不允许从浏览器直接访问他们的身份验证结构,我们需要使用服务器。 幸运的是,我们可以在自己的计算机上针对服务器进行开发,而不需要外部服务器。 我们在这本书中使用了大量的 JavaScript,所以让我们继续这个主题,并使用 node.js 在本地托管我们的站点。 任何其他 HTTP 服务器也可以工作。
安装 node.js 非常简单。 如果你使用的是 Windows 系统,那么可以从http://nodejs.org下载安装程序。 在 OS X 上,在同一个站点上有一个基于.pkg
的安装程序,或者它可以使用家酿软件安装。 如果您使用的是 Linux,最好是从源代码进行编译。 然而,如果你使用的是内置打包系统的发行版,比如apt或yum,那么有一个 node.js 包可以通过以下两个命令安装:
sudo yum install nodejs #Fedora or RedHat
sudo apt-get install nodejs #Debian or Ubunt
node.js 是一个服务器端软件,设计用来异步执行所有的 I/O 任务。 这意味着像写入磁盘这样的操作不会阻塞主线程。 当 I/O 完成时,会通知主线程。 最常见的应用之一是将其用作 HTTP 服务器。 这个功能以 HTTP 模块的形式出现,但是该模块提供的接口非常轻量级。 相反,我们将使用 Express 框架。 Express 是一个轻量级框架,它围绕路由、会话和服务内容以及模板提供了一些基础设施。 可以使用节点包管理器npm
进行安装,如下所示:
npm install express
今后我们将利用快递。
OAuth
当然,OAuth 可以手动配置和控制,但我们站在巨人的肩膀上是有充分理由的。 对我们来说,使用已经构建好的 OAuth 库要容易得多。 幸运的是,节点有这样一个库,创造性地称之为OAuth。 即使使用这个库,您也会发现与 OAuth 1.0a 端点的交互是复杂的。 要安装它,再次进入命令行并使用节点包管理器:
npm install oauth
这个库可以执行 OAuth 1.0a 和 OAuth 2.0 操作。 由于 Twitter 是 OAuth 1.0 的一个端点,我们将充分利用这一点。
首先要做的是设置 Express 应用。 Express 提供了应用模板,但对于本章中简单的应用来说,它们是多余的。 如果你打算在将来创建一个更复杂的应用,你会想要更多地了解应用生成和目录结构。 我们首先要求express
,并使用加载的模块创建一个新的应用,如下面的代码所示:
var express = require("express");
var app = express();
var oAuth = require('oauth');
Require 是一个允许动态加载 JavaScript 库的库。 这是在节点应用中引入外部模块的最常见方法。 接下来,我们在express
中配置一些设置,如下代码所示:
app.configure(function() {
app.use(express.bodyParser());
app.use(express.cookieParser() );
app.use(express.session({ secret: "a secret key"}));
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
bodyParser
允许express
对发送到服务器的请求体进行简单解析。 在下一行,设置cookieParser
。 与bodyParser
类似,它允许解析 cookie 并使用从 cookie 中检索的值填充请求对象,在我们的示例中,是会话的信息。 接下来,我们设置会话功能。 这使我们能够共享从一个请求到另一个请求的信息。 在其默认配置中,它使用内存存储来保存会话信息。 这意味着重新启动应用将删除会话信息。 如果你在一个机器群上托管你的可视化,你需要利用外部数据存储机制,如MongoDB或Redis。 我们传入一个用于生成session
散列的秘密密钥。 它应该是一个随机字符串。 使用app.router
将指示 express 监听路由请求,我们将在稍后定义。 最后,我们的.html
和.js
文件将放在一个名为public
的目录中,因此我们将指示express
将该目录的内容作为静态资源提供给用户。
我们现在想要利用 OAuth 库。 这可以通过如下代码所示的函数来实现:
function getOAuth(){
var twitterOauth = new oAuth.OAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
consumerKey,
consumerSecretKey,
'1.0A',
null,
'HMAC-SHA1');
return twitterOAuth;
}
我们创建一个与 Twitter 相关的 OAuth 对象。 我们给出两个端点,然后给出之前从 Twitter 收到的消费者密钥和消费者秘密。 OAuth 1.0a 需要嵌入消费者秘密,这就是为什么不能使用客户端代码从 Twitter 检索信息。 消费者的秘密不能通过发送给客户而泄露给外人。 1.0A
作为 OAuth 版本传入; 不需要授权回调,因此将null
作为第六个参数。 最后一个参数是签名方法:Twitter 使用HMAC-SHA1
。
接下来,我们将在 Express 应用中设置一条路由,从 Twitter 请求 OAuth 令牌:
app.get('/requestOAuth', function(req, res){
function recieveOAuthRequestTokens(error, oauth_token, oauth_token_secret,results) {
if (!error){
req.session.oAuthVars = { oauth_token: oauth_token,oauth_token_secret: oauth_token_secret}; res.redirect('https://api.twitter.com/oauth/authorize?oauth_token=' + oauth_token);
}
requestOAuthRequestTokens(recieveOAuthRequestTokens);
});
function requestOAuthRequestTokens(onComplete){
getOAuth().getOAuthRequestToken(onComplete);
}
这里,我们连接/requestOAuth
路由,首先请求一个 OAuth 令牌,然后使用它将用户重定向到 Twitter 上的登录页面。 我们构建一个匿名函数并将其传递给 OAuth,因为节点是高度异步的。 回调模型允许主节点线程服务另一个请求,同时等待 Twitter 用 OAuth 令牌返回给它。 一旦我们有了 OAuth 令牌,我们将它们保存在会话状态以供下一步使用,并重定向到 Twitter 授权页面。
一旦用户通过身份验证,Twitter 就会将用户重定向到我们在设置应用时定义的 URL。 在我们的例子中,这将由路由/receiveOAuth
提供,如下代码所示:
app.get('/receiveOAuth', function(req, res){
if(!req.session.oAuthVars){
res.redirect("/requestOAuth");
return;
}
if(!req.session.oAuthVars.oauth_access_token){
var oa = getOAuth();
oa.getOAuthAccessToken( req.session.oAuthVars.oauth_token, req.session.oAuthVars.oauth_token_secret, req.param('oauth_verifier'),
function(error, oauth_access_token, oauth_access_token_secret,tweetRes) {
req.session.oAuthVars.oauth_access_token = oauth_access_token;
req.session.oAuthVars.oauth_access_token_secret = oauth_access_token_secret;
GetRetweets(res, req.session.oAuthVars.oauth_access_token, req.session.oAuthVars.oauth_access_token_secret);
});
}
else
GetRetweets(res, req.session.oAuthVars.oauth_access_token, req.session.oAuthVars.oauth_access_token_secret);
});
这段代码将 Twitter 的redirect
传递回的 OAuth 令牌,并执行最后一步,即查找访问令牌。 一旦我们有了这些访问令牌,就可以使用它们在GetRetweets
函数中调用 API-here。 我们将在会话中保存所有生成的令牌,这样用户就不必不断授予对 Twitter API 的访问权。
厌倦了代币吗? 你应该! 这个交换设置 OAuth 1.0a 使用了大量的令牌。 幸运的是,我们已经完成了令牌和 OAuth。 现在我们可以开始用 Twitter 数据构建可视化了!
可视化
Twitter 为我们提供了大量的 api。 我们也许应该从发明一些我们想要可视化的东西开始,然后决定数据是否可用,以及我们如何展示它。 我很好奇我关注的人中谁的推特最多。 像@kellabyte
这样的账户似乎总是在发推特,而像@ericevans 这样的账户几乎不发推特。
服务器端
让我们从获取服务器端的数据开始。 在 node.js 中,我使用以下代码建立了一个新的路由:
app.get('/friends', function(req, res){
if(!req.session.oAuthVars || !req.session.oAuthVars.oauth_access_token){
res.redirect('/requestOAuth');
return;
}
var cursor = -1;
receiveUserListPage(res, req.session.twitterVars.user_id, req.session.oAuthVars.oauth_access_token, req.session.oAuthVars.oauth_access_token_secret, cursor, new Array());
});
首先,我们检查以确保会话中有适当的令牌可用。 如果没有,那么我们重定向回requestOAuth
页面,这将启动整个 OAuth 工作流。 接下来,我们设置一个初始游标值。 Twitter 限制了从其服务中返回的搜索结果的数量。 这避免了向消费者倾销大量记录,而这可能不是任何一方都想要的。 对于 API 调用,我们将使用设置为 20 的限制。 然而,Twitter 也提供了一个延续标记,他们称之为游标。 通过使用这个令牌再次调用服务,将返回结果的下一页。 初始值为-1
给出第一页。 游标和所有必需的标记被传递到receiveUserListPage
,它将执行实际的查找。
提示
速率限制
Twitter 限制了你向他们的服务发送请求的数量。 在开发可视化时,您可能会遇到这些限制。 等待 15 分钟再试一次。 在生产中,尝试缓存数据,这样您就不必如此频繁地查询 Twitter。
receiveUserListPage
代码如下:
function receiveUserListPage(res, user_id, oauth_access_token, oauth_access_token_secret, currentCursor, fullResults){
var oauth = getOAuth();
oauth.get( 'https://api.twitter.com/1.1/friends/list.json?skip_status=true&user_id=' + user_id + "&cursor=" + currentCursor,
oauth_access_token,
oauth_access_token_secret,
function (e, data, oaRes){
var jsonData = JSON.parse(data);
if(jsonData.errors){
projectResults(res, fullResults);
return;
}
fullResults = _.union(fullResults,
_.map(jsonData.users,
function(item){return { name: item.name,
count: item.statuses_count
}}));
if(jsonData.next_cursor == 0){
projectResults(res, fullResults);
}
else
ReceiveUserListPage(res, user_id, oauth_access_token, oauth_access_token_secret, jsonData.next_cursor, fullResults);
}
});
}
function projectResults(res, fullResults)
{
var selectedResults = _.first(_.sortBy(fullResults, function(item){return item.count;}).reverse(), 10);
res.end(JSON.stringify(selectedResults));
}
我们首先获取对 OAuth 库的引用,然后使用当前光标和当前user_id
来查询 Twitter。 我们使用的 API 调用从我关注的人的列表中返回 20 个用户。 结果作为字符串返回,因此我们使用JSON.parse
将其解析为一个对象。 如果结果对象包含一个名为errors
的字段,那么我们可能已经达到了速率限制,因此我们返回到目前为止我们拉下的所有内容。 因为这个 API 电话的收费上限只有15
,如果你跟踪超过 300 人,你就会达到上限。
如果有结果,则将其附加到当前数据集。 我们使用下划线的map
函数只从字段中选择两个。 这节省了带宽,并使调试更容易,因为从 Twitter 返回的对象是非常重量级的,带有许多无用的字段。 如果next_cursor
等于0
,则意味着我们已经到达列表的末尾,可以返回当前的名称和计数集。 否则,我们求助于该函数,给它新的游标、名称集和项。 一旦我们遇到可以返回的情况,我们调用projectResults
,它将 10 个拥有最多 tweets 的用户发送到 JSON 格式的客户端。
提示
下划线 js
下划线 JavaScript 库是一个小型库,使使用数组变得更加容易。 它添加了集合函数,如union
和intersect,
,以及函数式编程概念,如 map 和 reduce。 可从http://underscorejs.org/下载。
客户端
客户端可视化代码可以放置在我们之前指示 Express 作为静态内容提供的公共目录中。
我想从视觉上展示最活跃的推特用户。 一个很好的方法是使用气泡图,并使气泡越大,他们有越多的 tweet。 让我们来构建代码:
function visualize(data){
var graph = d3.select(".visualization")
.append("svg")
.attr("width", 1024)
.attr("height", 768);
var colorScale = d3.scale.category10();
calculateBubbles(data, 1024, 768);
var currentX = 0;
graph.selectAll(".bubble")
data(data)
enter()
append("circle")
.style("fill", function(x,y){return colorScale(y);})
.attr("cx", function(d){return d.cx;})
.attr("cy", function(d){ return d.cy;})
.attr("r", 0)
.attr("opacity", .5)
.transition()
.duration(750)
.attr("r", function(d){return d.radius;});
graph.selectAll(".label")
.data(data)
.enter()
.append("text")
.text(function(d){return d.name + "(" + d.count + ")";})
.attr("x", function(d){return d.cx;})
.attr("y", function(d){return d.cy;})
.attr("text-anchor", "middle");
}
如果您对 d3 有所了解,那么这些代码中的大部分看起来都很熟悉。 传入的数据数组是从节点服务中检索的。 首先,我们在页面上创建任意大小的 SVG 元素。 然后,我们设置一个颜色比例,使我们的可视化将很好地着色。 calculateBubbles
函数是一个辅助函数,用于计算气泡的位置。 它用圆的 x 和 y 坐标以及圆的半径来增加我们的数据数组。 我们不会在这里讨论这个,但是代码可以在 GitHub 上找到。 我们为每一个顶级推特用户创建一个气泡。 我们使用颜色比例给它上色,并使用来自数据数组的预计算值设置位置。 一开始,我们设置半径为0
,但随后我们使用一个过渡来扩大圆圈,以获得一个很好的加载效果。
对于每个圆,我们想要标记这个圆代表什么。 这是通过在圆心添加一个文本元素来实现的。
根据我追踪的 10 个最活跃的人,得出的图表如下:
每个人都发了超过 35000 次推特。
小结
现在,您应该能够设置一个新的应用来查询 Twitter,使用 node.js 上的 OAuth 库创建适当的 OAuth 令牌,并构建一个气泡图。 Twitter API 非常丰富,并且有许多潜在的可视化功能。 我相信我们可以想出几十种潜在的可视化方法。 没有比试验 API 更好的学习方法了,所以不要害怕弄得一团糟。
在下一章中,我们将看一看流行问答网站 Stack Overflow 上的可视化数据。 他们的 API 很大程度上是开放的,大多数查询都不需要认证,所以我们应该暂时不用使用 OAuth 甚至 node.js。
版权属于:月萌API www.moonapi.com,转载请注明出处