使用 Ruby 和 Twitter 进行数据挖掘

Twitter API 的有趣一面

Twitter不仅是出色的实时社交网络工具,还是成熟的丰富信息源头,可供用户进行数据挖掘。平均计算,Twitter 用户每天会生成 1 亿 4 千万条 tweet(Twitter 上的微博信息),这些 tweet 涉及各种各样的主题。本文主要介绍数据挖掘,并使用面向对象的 Ruby 语言来展示数据挖掘概念。

M. Tim Jones, 独立作家

M. Tim JonesM. Tim Jones 是一名嵌入式固件架构师,他还是 Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming(目前已发布第二版)、AI Application Programming(已发布第二版)和 BSD Sockets Programming from a Multilanguage Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开发到嵌入式架构设计,再到网络协议的开发。Tim 是 Intel 的一名平台架构师,他还是一位作家,居住在科罗拉多州 Longmont 市。



2011 年 11 月 16 日

2008 年 10 月,与其他许多人一样,出于好奇,我创建了一个 Twitter 帐户。与大多数人一样,我与朋友建立连接,随意进行一些搜索,以便更好地理解这项服务。使用 140 个字符进行通信似乎并不是使 Twitter 广受欢迎一条创意。一个不相关的事件帮助我理解了 Twitter 的真实价值。

2009 年 7 月初,我的 Web 托管提供者突然无法使用。经过漫无目的的 Web 搜索,我发现的信息将这一事故归咎于西雅图的 Fisher Plaza 发生的火灾。来自传统的、基于 Web 的源的信息比较滞后,而且没有说明服务何时才可能得到恢复。但是,搜索 Twitter 后,我发现了对于那起事故的个人描述,包括关于现场发生的情况的实时信息。例如,在我的托管服务恢复之前不久,就有一条 tweet 指出该建筑的外面配备了一台柴油发电机。

那时我才意识到,Twitter 的真正威力在于个人和团体之间的开放、实时信息通信。但是,在这种通信的表面下,是一个珍贵的信息宝藏,揭示用户的行为以及本地和全球级别上的趋势。我是在一些使用 Ruby 语言的简单脚本、Twitter gem 和一个针对 Twitter 的 API 包装器的上下文中探索这种实现的。我还将展示如何构建一些简单 mashups,使用其他 Web 服务和应用程序来显示数据。

Ruby 知识

如果您没有掌握美妙的 Ruby 语言的一些基础知识,请参见 参考资料 部分的链接。这些示例展示了 Ruby 的价值,以及它在有限的源代码行中蕴含巨大能量的能力。

Twitter 和 API

早期 Web 旨在进行人机交互,当今的 Web 旨在通过 Web 服务进行机器间交互。这些服务针对一些最流行的网站,比如从各种 Google 服务到 LinkedIn、Facebook 和 Twitter。Web 服务创建了一些 API,外部应用程序可以通过它们查询或操作网站上的内容。

Web 服务通过几种风格实现。当今最流行的风格之一是 Representational State Transfer (REST)。REST 实现位于著名的 HTTP 服务之上,它允许 HTTP 作为 RESTful 架构的中介存在(使用 GETPUTPOSTDELETE 等标准 HTTP 操作)。API for Twitter 是作为这个中介上的一个抽象而开发的。这样,您不需要理解 REST、HTTP 以及 XML 或 JSON 等数据格式,相反,只需将一个基于对象的接口简单地集成到 Ruby 语言中即可。


快速浏览 Ruby 和 Twitter

下面,我们来探索如何联用 Twitter API 和 Ruby。如果您和我一样,都在使用 Ubuntu Linux®,那么您可以使用 apt 框架。

要获取最新的完整 Ruby 发行版(一个大小约为 13MB 的下载),使用以下命令行:

$ sudo apt-get install ruby1.9.1-full

接下来,使用 gem 实用工具获取 Twitter gem:

$ sudo gem install twitter

现在,您已经拥有这个步骤所需的组件,下面,我们来执行一个 Twitter 包装器测试。对于这个演示,我们使用了一个名为 Interactive Ruby Shell (IRB) 的 shell。这个 shell 允许您执行 Ruby 命令,并实时试用该语言。IRB 拥有大量功能,但我们只将它用于简单试验。

清单 1 展示了一个与 IRB 之间的会话,为了便于阅读,清单 1 被划分为三个部分。第一部分(行 001 和 002)只是准备环境:导入必要的运行时元素(require 方法加载并执行已命名库)。下一行 (003) 演示如何使用 Twitter gem 显示来自 IBM® developerWorks® 的最新 tweet。如上所示,您使用 Client::Timeline 模块的 user_timeline 方法来显示一个 tweet。第一个示例演示 Ruby 的 “链方法” 功能。user_timeline 返回一个由 20 个 tweet 组成的数组,您将那些 tweet 串联到 first 方法中。这样做的目的是从该数组提取第一个 tweet(firstArray 类的一个方法)。从这个 tweet,您提取通过 puts 发射到输出的文本字段。

下一个部分(行 004)使用用户定义的位置字段,这是一个自由格式字段,用户可以在其中提供有用和无用位置信息。在这个示例中,User 模块提取位置字段约束的用户信息。

最后一个部分(行 005 以后)探索 Twitter::Search 模块。这个搜索模块提供一个非常丰富的用于搜索 Twitter 的界面。在这个示例中,您首先创建一个搜索实例(行 005),然后在行 006 指定一个搜索。您要搜索的是包含单词 why 并指向用户 LulzSec 的最新 tweet。生成的列表已经过缩减和编辑。这些搜索比较麻烦,这是因为搜索实例维护一些已定义的过滤器。可以通过执行 search.clear 清除这些过滤器。

清单 1. 通过 IRB 试验 Twitter API
$ irb
irb(main):001:0> require "rubygems"
=> true
irb(main):002:0> require "twitter"
=> true

irb(main):003:0> puts Twitter.user_timeline("developerworks").first.text
dW Twitter is saving #IBM over $600K per month: will #Google+ add to that? > 
http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app
=> nil

irb(main):004:0> puts Twitter.user("MTimJones").location
Colorado, USA
=> nil

irb(main):005:0> search = Twitter::Search.new
=> #<Twitter::Search:0xb7437e04 @oauth_token_secret=nil, 
    @endpoint="https://api.twitter.com/1/", 
    @user_agent="Twitter Ruby Gem 1.6.0", 
    @oauth_token=nil, @consumer_secret=nil, 
    @search_endpoint="https://search.twitter.com/", 
    @query={:tude=>[], :q=>[]}, @cache=nil, @gateway=nil, @consumer_key=nil, 
    @proxy=nil, @format=:json, @adapter=:net_http<
irb(main):006:0> search.containing("why").to("LulzSec").
result_type("recent").each do |r| puts r.text end
@LulzSec why not stop posting <bleep> and get a full time job! MYSQLi isn't 
hacking you <bleep>.
...
irb(main):007:0>

下面,我们来看看 Twitter 中的一个用户的模式。您可以通过 IRB 来执行这个任务,但我将重新格式化结果,更简单地演示 Twitter 用户的结构。清单 2 展示了打印用户结构的结果,该结构在 Ruby 中为一个 Hashie::Mash。这种结构很有用,因为它允许对象拥有类似方法的散列键访问符(开放对象)。从 清单 2 可以看出,这个对象包含丰富的信息(特定于用户的信息和呈现信息),其中包括当前用户状态(包含地理编码信息)。一个 tweet 也可以包含大量信息,您可以轻松可视化如何使用 user_timeline 类生成这个信息。

清单 2. 一个 Twitter 用户的结构(Ruby 视角)
irb(main):007:0> puts Twitter.user("MTimJones")
<#Hashie::Mash 
  contributors_enabled=false 
  created_at="Wed Oct 08 20:40:53 +0000 2008" 
  default_profile=false default_profile_image=false 
  description="Platform Architect and author (Linux, Embedded, Networking, AI)."
  favourites_count=1 
  follow_request_sent=nil 
  followers_count=148 
  following=nil 
  friends_count=96 
  geo_enabled=true 
  id=16655901 id_str="16655901" 
  is_translator=false 
  lang="en" 
  listed_count=10 
  location="Colorado, USA" 
  name="M. Tim Jones" 
  notifications=nil 
  profile_background_color="1A1B1F" 
  profile_background_image_url="..."
  profile_background_image_url_https="..." 
  profile_background_tile=false 
  profile_image_url="http://a0.twimg.com/profile_images/851508584/bio_mtjones_normal.JPG" 
  profile_image_url_https="..." 
  profile_link_color="2FC2EF" 
  profile_sidebar_border_color="181A1E" profile_sidebar_fill_color="252429" 
  profile_text_color="666666" 
  profile_use_background_image=true 
  protected=false 
  screen_name="MTimJones" 
  show_all_inline_media=false 
  status=<#Hashie::Mash 
    contributors=nil coordinates=nil 
    created_at="Sat Jul 02 02:03:24 +0000 2011" 
    favorited=false 
    geo=nil 
    id=86978247602094080 id_str="86978247602094080" 
    in_reply_to_screen_name="AnonymousIRC" 
    in_reply_to_status_id=nil in_reply_to_status_id_str=nil 
    in_reply_to_user_id=225663702 in_reply_to_user_id_str="225663702" 
    place=<#Hashie::Mash 
      attributes=<#Hashie::Mash> 
      bounding_box=<#Hashie::Mash 
        coordinates=[[[-105.178387, 40.12596], 
                      [-105.034397, 40.12596], 
                      [-105.034397, 40.203495], 
                      [-105.178387, 40.203495]]] 
        type="Polygon"
      > 
      country="United States" country_code="US" 
      full_name="Longmont, CO" 
      id="2736a5db074e8201" 
      name="Longmont" place_type="city" 
      url="http://api.twitter.com/1/geo/id/2736a5db074e8201.json"
    > 
    retweet_count=0 
    retweeted=false 
    source="web" 
    text="@AnonymousIRC @anonymouSabu @LulzSec @atopiary @Anonakomis Practical reading 
          for future reference... LULZ \"Prison 101\" http://t.co/sf8jIH9" truncated=false
  >
  statuses_count=79 
  time_zone="Mountain Time (US & Canada)" 
  url="http://www.mtjones.com" 
  utc_offset=-25200 
  verified=false
>
=> nil
irb(main):008:0>

我们的快速浏览到此为止。现在,我们来看一些简单的脚本,您可以使用它们通过 Ruby 和 Twitter API 收集和可视化数据。在此过程中,您将了解一些 Twitter 概念,比如身份验证和等级限制。


挖掘 Twitter 数据

下面几个小节将演示如何使用几个脚本来收集和显示通过 Twitter API 获取的数据。这些脚本主要关注简单性,但您也可以扩展并合并它们来创建新功能。另外,本节涉及功能强大的 Twitter gem API 的一些皮毛。

需要重点注意的是,这个 Twitter API 只允许在给定的一小时内进行数量有限的调用,即 Twitter 等级限制 (rate-limits ) 请求(目前不超过每小时 150 个),这意味着,使用到一定的量后,您将获得一条错误消息,需要等待一定时间才能提交新请求。

用户信息

回顾一下 清单 2,对于每个 Twitter 用户,都可以获取大量信息。这个信息仅当用户不受保护时才允许访问。我们来看看如何提取用户数据并以一种更方便的方式显示数据。

清单 3 演示了如何使用一个简单的 Ruby 脚本检索一个用户的信息(基于其屏幕姓名),然后发射一些更有用的元素。您可以使用 to_s Ruby 方法,根据需要将这个值转换为一个字符串。注意,首先需要确保用户不受保护,否则不允许访问数据。

清单 3. 提取 Twitter 用户数据的简单脚本 (user.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"

screen_name = String.new ARGV[0]

a_user = Twitter.user(screen_name)

if a_user.protected != true

  puts "Username   : " + a_user.screen_name.to_s
  puts "Name       : " + a_user.name
  puts "Id         : " + a_user.id_str
  puts "Location   : " + a_user.location
  puts "User since : " + a_user.created_at.to_s
  puts "Bio        : " + a_user.description.to_s
  puts "Followers  : " + a_user.followers_count.to_s
  puts "Friends    : " + a_user.friends_count.to_s
  puts "Listed Cnt : " + a_user.listed_count.to_s
  puts "Tweet Cnt  : " + a_user.statuses_count.to_s
  puts "Geocoded   : " + a_user.geo_enabled.to_s
  puts "Language   : " + a_user.lang
  puts "URL        : " + a_user.url.to_s
  puts "Time Zone  : " + a_user.time_zone
  puts "Verified   : " + a_user.verified.to_s
  puts

  tweet = Twitter.user_timeline(screen_name).first

  puts "Tweet time : " + tweet.created_at
  puts "Tweet ID   : " + tweet.id.to_s
  puts "Tweet text : " + tweet.text

end

要调用这个脚本,首先应确保它是可执行的(chmod +x user.rb),然后通过一个用户调用它。清单 4 显示了针对 developerworks 用户的结果,其中包含用户信息和当前状态(最后的 tweet 信息)。这里要注意,Twitter 将关注您的人定义为关注者,将您跟随的人称为朋友

清单 4. user.rb 的样例输出
$ ./user.rb developerworks
Username   : developerworks
Name       : developerworks
Id         : 16362921
Location   : 
User since : Fri Sep 19 13:10:39 +0000 2008
Bio        : IBM's premier Web site for Java, Android, Linux, Open Source, PHP, Social, 
Cloud Computing, Google, jQuery, and Web developer educational resources
Followers  : 48439
Friends    : 46299
Listed Cnt : 3801
Tweet Cnt  : 9831
Geocoded   : false
Language   : en
URL        : http://bit.ly/EQ7te
Time Zone  : Pacific Time (US & Canada)
Verified   : false

Tweet time : Sun Jul 17 01:04:46 +0000 2011
Tweet ID   : 92399309022167040
Tweet text : dW Twitter is saving #IBM over $600K per month: will #Google+ add to that? > 
http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app

朋友受欢迎程度

查看您的朋友(您跟随的人),收集数据以理解他们的受欢迎程度。在本例中,您收集您的朋友,并按照他们的关注者计数进行排序。这个简单脚本如 清单 5 所示。

在这个脚本中,您在理解要分析的用户后(基于他们的屏幕姓名),创建一个用户散列表。Ruby 散列表(或关联数组)是一种数据结构,允许您定义用于存储的键(而不是简单的数值索引)。然后,您的散列表按照 Twitter 屏幕姓名索引,关联值是用户的关注者计数。这个过程仅仅是迭代您的朋友并散列化 (hash) 它们的关注者计数。(按降序)排列您的散列表,然后将其作为输出发送出去。

清单 5. 朋友的受欢迎程度的脚本 (friends.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"

name = String.new ARGV[0]

user = Hash.new

# Iterate friends, hash their followers
Twitter.friends(name).users.each do |f|

  # Only iterate if we can see their followers
  if (f.protected.to_s != "true") 
    user[f.screen_name.to_s] = f.followers_count 
  end

end

user.sort_by {|k,v| -v}.each { |user, count| puts "#{user}, #{count}" }

清单 5 中的朋友脚本的样例输出如 清单 6 所示。为节约空间,我对输出进行了删减,但可以看到,ReadWriteWeb (RWW) 和 Playstation 是我的直接网络中的受欢迎 Twitter 用户。

清单 6. 清单 5 中的朋友脚本的屏幕输出
$ ./friends.rb MTimJones
RWW, 1096862
PlayStation, 1026634
HarvardBiz, 541139
tedtalks, 526886
lifehacker, 146162
wandfc, 121683
AnonymousIRC, 117896
iTunesPodcasts, 82581
adultswim, 76188
forrester, 72945
googleresearch, 66318
Gartner_inc, 57468
developerworks, 48518

我的关注者在哪里?

回顾一下 清单 2,Twitter 提供丰富的位置信息。有一个位置字段,它是自由格式的、用户定义的以及可选的地理编码数据。但是,用户定义的时区还可以提供关于关注者的实际位置的暗示。

在本例中,您构建了一个 mash-up,它从您的 Twitter 关注者提取时区数据,然后使用 Google Charts 可视化该数据。Google Charts 是一个有趣的项目,它允许您在 Web 上构建各种不同图表类型;将图表类型和数据定义为一个 HTTP 请求,结果直接作为响应在浏览器中呈现。要安装 Ruby gem for Google Charts,使用以下命令行:

$ gem install gchartrb

清单 7 提供的脚本首先提取时区数据,然后构建 Google Charts 请求。与前面的脚本不同,该脚本要求您通过 Twitter 身份验证。为此,您需要向 Twitter 注册一个应用程序,然后 Twitter 会为您提供一组密匙和令牌。可以将这些令牌应用于清单 7 中的脚本,以便成功提取数据。请参见 参考资料,了解关于这个轻松过程的详细信息。

这个脚本遵循一种类似模式,接受一个屏幕姓名,然后迭代该用户的关注者。当前关注者的时区被提取并存储在 tweetlocation 散列表中。注意,您首先测试这个密匙是否位于散列表中,如果是,则递增该密匙的计数器。您还可以保持一个关于总时区数的标签,以便将来创建百分比。

这个脚本的最后一部分是构造 Google Pie Chart URL。您创建了一个新 PieChart 并指定了一些选项(大小、标题以及是否为 3D)。然后,您迭代您的时区散列表,向图表发送数据,创建时区字符串(移除 & 符号)和该时区占总时区数的百分比。

清单 7. 从 Twitter 关注者的时区构建一个饼图 (followers-location.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

screen_name = String.new ARGV[0]

tweetlocation = Hash.new
timezones = 0.0

# Authenticate
Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = <oauth_token>'
  config.oauth_token_secret = '<oauth_token_secret>'
end

# Iterate followers, hash their location
followers = Twitter.followers.users.each do |f|

  loc = f.time_zone.to_s

  if (loc.length > 0)

    if tweetlocation.has_key?(loc)
      tweetlocation[loc] = tweetlocation[loc] + 1
    else
      tweetlocation[loc] = 1
    end

    timezones = timezones + 1.0

  end

end

# Create a pie chart
GoogleChart::PieChart.new('650x350', "Time Zones", false ) do |pc|

  tweetlocation.each do |loc,count|
    pc.data loc.to_s.delete("&"), (count/timezones*100).round
  end

  puts pc.to_url

end

要执行清单 7 中的脚本,首先向它提供一个 Twitter 屏幕姓名,然后复制生成的 URL 并将其粘贴到一个浏览器中。清单 8 展示了这个过程和生成的 URL。

清单 8. 调用 followers-location 脚本(结果只有一行)
$ ./followers-location.rb MTimJones
http://chart.apis.google.com/chart?chl=Seoul|Santiago|Paris|Mountain+Time+(US++Canada)|
Madrid|Central+Time+(US++Canada)|Warsaw|Kolkata|London|Pacific+Time+(US++Canada)|
New+Delhi|Pretoria|Quito|Dublin|Moscow|Istanbul|Taipei|Casablanca|Hawaii|Mumbai|
International+Date+Line+West|Tokyo|Ulaan+Bataar|Vienna|Osaka|Alaska|Chennai|Bern|
Brasilia|Eastern+Time+(US++Canada)|Rome|Perth|La+Paz
&chs=650x350&chtt=Time+Zones&chd=s:KDDyKcKDOcKDKDDDDDKDDKDDDDOKK9DDD&cht=p
$

当您将清单 8 中的 URL 粘贴到浏览器中时,您将得到 图 1 中显示的结果。

图 1. Twitter 关注者的位置的饼图
饼图显示按时区组织的关注者的国家

Twitter 用户行为

Twitter 包含大量数据,您可以挖掘它们,以理解用户行为的某些元素。以下是两个简单示例,展示了用户何时发出 tweet 和从什么应用程序发出 tweet。您可以使用以下两个简单脚本来提取这个信息并将其可视化。

清单 9 展示的脚本(使用 user_timeline 方法)迭代来自一个特定用户的 tweet,然后提取每个 tweet 生成的日期。您可以再次使用一个简单的散列表来累计您的工作日计数,然后以类似于前面时区示例的方式使用 Google Charts 生成一个条形图。

清单 9. 构建一个 tweet 天数条形图 (tweet-days.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require "google_chart"

screen_name = String.new ARGV[0]

dayhash = Hash.new

timeline = Twitter.user_timeline(screen_name, :count => 200 )
timeline.each do |t|

  tweetday = t.created_at.to_s[0..2]

  if dayhash.has_key?(tweetday)
    dayhash[tweetday] = dayhash[tweetday] + 1
  else
    dayhash[tweetday] = 1
  end

end

GoogleChart::BarChart.new('300x200', screen_name, :vertical, false) do |bc|
  bc.data "Sunday", [dayhash["Sun"]], '00000f'
  bc.data "Monday", [dayhash["Mon"]], '0000ff'
  bc.data "Tuesday", [dayhash["Tue"]], '00ff00'
  bc.data "Wednesday", [dayhash["Wed"]], '00ffff'
  bc.data "Thursday", [dayhash["Thu"]], 'ff0000'
  bc.data "Friday", [dayhash["Fri"]], 'ff00ff'
  bc.data "Saturday", [dayhash["Sat"]], 'ffff00'
  puts bc.to_url
end

图 2 提供针对 developerWorks 帐户执行 tweet-days 脚本的结果。如图所示,星期三可能是最活跃的 tweet 工作日,而星期六和星期日可能最不活跃。

图 2. 每天 tweet 活动的相对条形图
条形图显示每周各天的活动

下一个脚本可确定一个特定用户的 tweet 来自哪个源。您可以通过几种方式提交 tweet,该脚本没有对它们进行完全编码。如 清单 10 所示,您可以使用一种类似的模式来提取某个特定用户的用户时间线,然后试着在散列表中解码 tweet 的源代码。随后使用这个散列表来创建一个简单的饼图,再使用 Google Charts 将数据可视化。

清单 10. 构建一个用户的 tweet 源的饼图 (tweet-source.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

screen_name = String.new ARGV[0]

tweetsource = Hash.new

timeline = Twitter.user_timeline(screen_name, :count => 200 )
timeline.each do |t|

  if (t.source.rindex('blackberry')) then
    src = 'Blackberry'
  elsif (t.source.rindex('snaptu')) then
    src = 'Snaptu'
  elsif (t.source.rindex('tweetmeme')) then
    src = 'Tweetmeme'
  elsif (t.source.rindex('android')) then
    src = 'Android'
  elsif (t.source.rindex('LinkedIn')) then
    src = 'LinkedIn'
  elsif (t.source.rindex('twitterfeed')) then
    src = 'Twitterfeed'
  elsif (t.source.rindex('twitter.com')) then
    src = 'Twitter.com'
  else
    src = t.source
  end

  if tweetsource.has_key?(src)
    tweetsource[src] = tweetsource[src] + 1
  else
    tweetsource[src] = 1
  end

end

GoogleChart::PieChart.new('320x200', "Tweet Source", false) do |pc|

  tweetsource.each do|source,count|
    pc.data source.to_s, count
  end

  puts "\nPie Chart"
  puts pc.to_url
end

图 3 提供了一个 Twitter 用户的视觉表示,该用户拥有一组有趣的 tweet 源。使用最频繁的是传统 Twitter 网站,其次是一个移动电话应用程序。

图 3. 一个 Twitter 用户的 tweet 源的饼图
饼图显示用于生成 tweet 的工具,比如 twitter.com、web、LinkedIn 等

关注者图表

Twitter 是一个大众用户网络,这个网络形成了一个图表。从上面的脚本可以看出,可以轻松迭代您的联系人,然后迭代联系人的联系人。这样就形成了一个大型图表的基础,甚至可以在此级别上进行迭代。

为可视化一个图表,我选择使用图表可视化软件 GraphViz。在 Ubuntu 上,可以使用以下命令行轻松安装这个工具:

$ sudo apt-get install graphviz

清单 11 展示的脚本首先迭代一个用户的关注者,然后迭代他们的关注者。这种模式的唯一区别是需要构造一个 GraphViz 点格式文件。GraphViz 使用一种简单的脚本格式来定义一些图表,这些图表将作为您的 Twitter 用户枚举的一部分。如清单 11 所示,要定义一个图表,只需指定节点的关系。

清单 11. 可视化一个 Twitter 关注者图表 (followers-graph.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

screen_name = String.new ARGV[0]

tweetlocation = Hash.new

# Authenticate
Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = '<oauth_token>'
  config.oauth_token_secret = '<oauth_token_secret>'
end

my_file = File.new("graph.dot", "w")

my_file.puts "graph followers {"
my_file.puts "  node [ fontname=Arial, fontsize=6 ];"

# Iterate followers, hash their location
followers = Twitter.followers(screen_name, :count=>10 ).users.each do |f|

  # Only iterate if we can see their followers
  if (f.protected.to_s != "true")

    my_file.puts "  \"" + screen_name + "\" -- \"" + f.screen_name.to_s + "\""

    followers2 = Twitter.followers(f.screen_name, :count =>10 ).users.each do |f2|

      my_file.puts "    \"" + f.screen_name.to_s + "\" -- \"" +
                    f2.screen_name.to_s + "\""

    end

  end

end

my_file.puts "}"

执行清单 11 中的脚本将生成一个点文件,稍后将使用 GraphViz 从这个点文件生成一个图像。首先,调用这个 Ruby 脚本收集图表数据(存储为 graph.dot);然后,使用 GraphViz 生成图表图像(这里使用 circo,它指定一个圆形布局)。这个图像的生成流程定义如下:

$ ./followers-graph.rb MTimJones
$ circo graph.dot -Tpng -o graph.png

生成的图像如 图 4 所示。注意,Twitter 图表可能比较大,因此我对图表进行了限制,最小化要枚举的用户数量及其关注者(采用 清单 11 中的 :count 选项)。

图 4. 样例 Twitter 关注者图表(极子集)
关注者图表将关注者显示为一些互连的集线器,就像一个网络图表

位置信息

如果启用,Twitter 会收集您和您的 tweet 的地理位置数据。这些数据包含纬度和经度信息,可用于定位用户和确定 tweet 的来源地。而且,搜索可以包含这些信息,以便基于已定义位置或您的位置识别地点或用户。

并非所有用户都支持地理定位(由于隐私原因),但这些信息提现了整个 Twitter 体验的一个有趣方面。下面我们来看两个脚本:一个使用地理位置数据进行可视化;另一个使用地理位置信息进行搜索。

在第一个脚本中(如 清单 12 所示),您从一个用户获取纬度和经度数据(回想一下 清单 2 中的边界框)。尽管这个边界框是一个用于定义表示用户的区域的多边形,但我简化并使用这些数据的一个点。使用这些数据,我在一个简单的 HTML 文件中生成了一个简单 JavaScript 函数。这个 JavaScript 代码与 Google Maps 交互,显示这个位置的一个俯视地图(前提是拥有从 Twitter 用户提取的纬度和经度数据)。

清单 12. 构建用户地图的 Ruby 脚本 (where-am-i.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"

Twitter.configure do |config|
  config.consumer_key = '<consumer_key>
  config.consumer_secret = '<consumer_secret>
  config.oauth_token = '<oauth_token>
  config.oauth_token_secret = '<token_secret>
end

screen_name = String.new ARGV[0]

a_user = Twitter.user(screen_name)

if a_user.geo_enabled == true

  long = a_user.status.place.bounding_box.coordinates[0][0][0];
  lat  = a_user.status.place.bounding_box.coordinates[0][0][1];

  my_file = File.new("test.html", "w")

  my_file.puts "<!DOCTYPE html>
  my_file.puts "<html><head>
  my_file.puts "<meta name=\"viewport\" content=\"initial-scale=1.0, user-scalable=no\"/>"
  my_file.puts "<style type=\"text/css\">"
  my_file.puts "html { height: 100% }"
  my_file.puts "body { height: 100%; margin: 0px; padding: 0px }"
  my_file.puts "#map_canvas { height: 100% }"
  my_file.puts "</style>"
  my_file.puts "<script type=\"text/javascript\""
  my_file.puts "src=\"http://maps.google.com/maps/api/js?sensor=false\">"
  my_file.puts "</script>"
  my_file.puts "<script type=\"text/javascript\">"
  my_file.puts "function initialize() {"
  my_file.puts "var latlng = new google.maps.LatLng(" + lat.to_s + ", " + long.to_s + ");"
  my_file.puts "var myOptions = {"
  my_file.puts "zoom: 12,"
  my_file.puts "center: latlng,"
  my_file.puts "mapTypeId: google.maps.MapTypeId.HYBRID"
  my_file.puts "};"
  my_file.puts "var map = new google.maps.Map(document.getElementById(\"map_canvas\"),"
  my_file.puts "myOptions);"
  my_file.puts "}"
  my_file.puts "</script>"
  my_file.puts "</head>"
  my_file.puts "<body onload=\"initialize()\">"
  my_file.puts "<div id=\"map_canvas\" style=\"width:100%; height:100%\"></div>"
  my_file.puts "</body>"
  my_file.puts "</html>"

else

  puts "no geolocation data available."

end

要执行清单 12 中的脚本,只需使用以下命令行:

$ ./where-am-i.rb MTimJones

生成的 HTML 文件通过一个浏览器呈现:

$ firefox test.html

如果没有可用的位置信息,这个脚本就会失败;如果成功,脚本将生成一个 HTML 文件,浏览器可以读取该文件来呈现一个地图。图 5 显示了生成的地图图像,该图像显示美国科罗拉多州北部 Front Range 的一部分。

图 5. 从清单 12 中的脚本呈现的样例图像
所选地区的 Google 卫星地图,没有特殊标记或标签

使用地理位置数据,还可以搜索 Twitter,识别与特定位置相关的 Twitter 用户和 tweet。Twitter Search API 允许使用地理编码信息来限制其结果。下面的 清单 13 中的示例提取一个用户的纬度和经度数据,然后使用该数据获取该位置周围半径 5 英里范围内的 tweet。

清单 13. 使用纬度和经度数据搜索当地 tweet (tweets-local.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"

Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = '<oauth_token>'
  config.oauth_token_secret = '<oauth_token_secret>'
end

screen_name = String.new ARGV[0]

a_user = Twitter.user(screen_name)

if a_user.geo_enabled == true

  long = a_user.status.place.bounding_box.coordinates[0][0][0]
  lat  = a_user.status.place.bounding_box.coordinates[0][0][1]

  Array tweets = Twitter::Search.new.geocode(lat, long, "5mi").fetch

  tweets.each do |t|

    puts t.from_user + " | " + t.text

  end

end

清单 13 中的脚本的结果如 清单 14 所示。这是指定频率的 Twitter 用户的 tweet 的子集。

清单 14. 查看我的位置周围半径 5 英里内的本地 tweet
$ ./tweets-local.rb MTimJones
Breesesummer | @DaltonOls did he answer u
LongmontRadMon | 60 CPM, 0.4872 uSv/h, 0.6368 uSv/h, 2 time(s) over natural radiation
graelston | on every street there is a memory; a time and place we can never be again.
Breesesummer | #I'minafight with @DaltonOls to see who will marry @TheCodySimpson I will 
marry him!!! :/
_JennieJune_ | ok I'm done, goodnight everyone!
Breesesummer | @DaltonOls same
_JennieJune_ | @sylquejr sleep well!
Breesesummer | @DaltonOls ok let's see what he says
LongmontRadMon | 90 CPM, 0.7308 uSv/h, 0.7864 uSv/h, 2 time(s) over natural radiation
Breesesummer | @TheCodySimpson would u marry me or @DaltonOls
natcapsolutions | RT hlovins: The scientific rebuttal to the silly Forbes release this 
morning: Misdiagnosis of Surface Temperatu... http://bit.ly/nRpLJl
$

结束语

本文展示了如何通过几个简单脚本和 Ruby 语言从 Twitter 提取数据。本文主要通过开发和呈现几个简单脚本来展示一些基本概念,但是,除了给出的示例之外,还有更多可能性。例如,您还可以使用这个 API 来探索您的朋友网络,认识您感兴趣的、最受欢迎的 Twitter 用户。另一个有趣的领域是挖掘 tweet 本身,使用地理位置数据来理解基于位置的行为或事件(比如流行感冒的爆发)。本文只触及这个主题的表面,您完全可以通过您自己的 mash-ups 在下面发表评论。Ruby 和 Twitter gem 允许您轻松开发有用的 mash-ups 或指示板,从而满足您的数据挖掘需求。

参考资料

学习

  • Ruby 的官方语言网站 是 Ruby 语言的新闻、信息、发布、文档和社区支持的发源地。鉴于 Ruby 在 Web 框架中的广泛应用(比如 Ruby on Rails),您还可以了解最新的安全漏洞及其解决方案。
  • Github 社交编码站点 提供了 Twitter gem 官方源代码。在这个站点中,您可以访问 Ruby Twitter gem 的源代码、文档以及邮件列表。
  • 注册一个 Twitter 应用程序 是使用某些 Twitter API 元素的必要条件。注册是免费的,注册后可以访问一些更有用的 API 元素。
  • Google Maps JavaScript API 教程 介绍如何使用 Google Maps 和用户提供的地理位置数据呈现各种类型的地图。本文使用的 JavaScript 基于此教程中提供的 "Hello World" 示例代码。
  • developerWorks 演示中心:观看面向初学者的产品安装和设置演示,了解经验丰富的开发人员使用的一些高级功能。
  • 随时关注 developerWorks 技术活动网络广播
  • 访问 developerWorks Open source 专区获得丰富的 how-to 信息、工具和项目更新以及最受欢迎的文章和教程,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。

获得产品和技术

  • Twitter Ruby gem 由 John Nunemaker 开发,提供一个有用的 Twitter 服务接口,该接口可以轻松集成到 Ruby 语言中。
  • Google Chart API 是一个有用的服务,支持使用各种样式和选项构造复杂丰富的图表。这个服务提供一个 API,可以通过该 API 在 Google 站点上呈现 URL 结果。
  • Google Chart API Ruby 包装器 向 Google Charts API 提供一个 Ruby 接口,支持在 Ruby 中构造有用的图表。
  • 以最适合您的方式 IBM 产品评估试用版软件:下载产品试用版,在线试用产品,在云环境下试用产品,或者在 IBM SOA 人员沙箱 中花费几个小时来学习如何高效实现面向服务架构。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=775330
ArticleTitle=使用 Ruby 和 Twitter 进行数据挖掘
publish-date=11162011