2008年10月,我像许多人一样满怀好奇地创建了一个Twitter帐户。像大多数人一样,我与一些朋友建立了连接,做一些随机的搜索来更好地理解这一服务。使用140个字符来进行沟通,这看起来好像不太可能是一个受欢迎的想法,但一件无关的事情却让我看到了Twitter的真正价值所在。

2009年7月初,我的网站托管服务提供商熄火了,在进行了一番随意的网络搜索后,我发现信息指向了西雅图Fisher Plaza的一场火灾,这场火是罪魁祸首。传统的基于web的信息源更新很慢,没有提供何时服务会恢复正常的指示。然而,在搜索了Twitter之后,我找到了一些个人帐户对事件的描述,包括了现场实时发生的情况。例如,在我的托管服务恢复正常之前不久,就有一条消息说有柴油发电机位于大厦的外面。

当时我就意识到了Twitter的真正强大之处在于个人以及组织之间的公开的和实时的信息交流。然而,在这一表象下面,则是一个关于用户行为的信息宝库,以及在局部和全局层面上的发展趋势。我使用了Ruby语言和Twitter gem这一Twitter的API包装器来在简单的脚本语境中探讨挖掘的实现,并说明了如何使用其他的web服务和应用来为数据的可视化构建一个简单的混搭应用程序。

Twitter以及一些API

尽管早期的网络涉及的是人-机器的交互,但现在的网络已涉及机器-机器之间的交互,这种交互是使用web服务来支持的。大部分受欢迎的网站都有这样的服务存在——从各种各样的Google服务到LinkedIn、Facebook和Twitter等。通过web服务创建的API,外部的应用可以查询或是操纵网站上的内容。

web服务可以使用多种方式来实现。目前最流行的做法之一是表述性状态转移(Representational State Transfe, REST)。REST的一种实现是通过为人熟知的HTTP协议,允许HTTP作为RESTful架构的媒介存在(使用诸如GET、PUT、POST、DELETE一类的标准HTTP操作)。Twitter的API是作为这一媒介之上的一个抽象来进行开发的。在这一做法中,没有涉及REST、HTTP或是XML或JSON一类的数据格式的知识,而是代之以清晰地整合到了Ruby语言中的基于对象的接口。

Ruby和Twitter的一个快速展示

让我们来探讨一下如何在Ruby中使用Twitter API。首先,我们需要获取所需的资源,如果你像我一样在用Ubuntu Linux®的话,就使用apt框架。

若要获取最新的完整的Ruby分发版本(大约13MB的下载量),使用这一命令行:

  1. $ sudo apt-get install ruby1.9.1-full 

接着使用gem实用程序来抓取Twitter gem:

  1. $ sudo gem install twitter 

现在你已经有了这一步骤所需的一切了,我们继续,测试一下Twitter的包装器。这一示例使用一个名为交互式的Ruby外壳(Interactive Ruby Shell,IRB)的外壳程序,该外壳程序允许实时地执行Ruby命令以及使用语言进行实验。IRB有着非常多的功能,不过我们只用它来做一些简单的实验。

清单1展示了与IRB的一个会话,该会话被分成了三段以便于阅读。第一段(001和002行)通过导入必需的运行时元素来做好环境方面的准备(require方法加载并执行指定的库)。接下来的一段(003行)说明的是使用Twitter gem来显示从IBM® developerWorks®发出的最新tweet消息。如所展示的那样,使用Client::Timeline模块的user_timeline方法来显示一条消息,这第一个例子说明了Ruby的“链方法”的功能。user_timeline方法返回一个有着20条消息的数组,接着链入到方法first中,这样做是为了从数组中提取出第一条消息(first是Array类的一个方法),接着从这一条消息中提取出文本字段,通过puts方法把它放到输出中。

接下来的一段(004行)使用了用户定义的位置字段,这是一个不限形式的字段,用户可以在其中提供有用的或是无用的位置信息。在这一例子中,User模块抓取了位置字段所限定的用户信息。

最后一段(从005行开始)研究了Twitter::Search模块,这一搜索模块提供了极其丰富的用来搜索Twitter的接口。在这一例子中,首先是创建一个搜索实例(005行),接着在006行指定一个搜索,搜 LulzSec用户在最近发出的包含了why这一词的消息,结果列表已经过删减和编辑。搜索设置会一直存在在那里,因为搜索实例保持着所定义的过滤条件,你可以通过执行search.clear来清除这些过滤条件。

清单1. 通过IRB来实验Twitter API

  1. $ irb  
  2. irb(main):001:0> require "rubygems" 
  3. => true 
  4. irb(main):002:0> require "twitter" 
  5. => true 
  6.  
  7. irb(main):003:0> puts Twitter.user_timeline("developerworks").first.text  
  8. dW Twitter is saving #IBM over $600K per month: will #Google+ add to that?>   
  9. http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app  
  10. => nil 
  11.  
  12. irb(main):004:0> puts Twitter.user("MTimJones").location  
  13. Colorado, USA  
  14. => nil 
  15.  
  16. irb(main):005:0> search = Twitter::Search.new 
  17. => #<Twitter::Search:0xb7437e04 @oauth_token_secret=nil,   
  18. @endpoint="https://api.twitter.com/1/",   
  19. @user_agent="Twitter Ruby Gem 1.6.0",   
  20. @oauth_token=nil, @consumer_secret=nil,   
  21. @search_endpoint="https://search.twitter.com/",   
  22. @query={:tude=>[], :q=>[]}, @cache=nil, @gateway=nil, @consumer_key=nil,   
  23. @proxy=nil, @format=:json, @adapter=:net_http<   
  24. irb(main):006:0> search.containing("why").to("LulzSec").  
  25. result_type("recent").each do |r| puts r.text end 
  26. @LulzSec why not stop posting <bleep> and get a full time job! MYSQLi isn't   
  27. hacking you <bleep>.  
  28. ...  
  29. irb(main):007:0> 

接下来,我们来看一下Twitter中的用户的模式,你也可以通过IRB来实现这一点,不过我重排了结果的格式,以便简化对Twitter用户的内部结构的说明。清单2给出了用户结构的输出结果,这在Ruby中是一个Hashie::Mash。这一结构很有用,因为其允许对象有类方法的哈希键访问器(公开的对象)。正如你从清单2中看到的那样,这一对象包含了丰富的信息(用户特定的以及渲染的信息),其中包括了当前的用户状态(带有地理编码信息)。一条tweet消息中也包含了大量的信息,你可以使用user_timeline类来轻松地可视化生成这一信息。

清单2. Twitter用户的内部解析结构(Ruby视角)

  1. irb(main):007:0> puts Twitter.user("MTimJones")  
  2. <#Hashie::Mash   
  3. contributors_enabled=false   
  4. created_at="Wed Oct 08 20:40:53 +0000 2008"   
  5. default_profile=false default_profile_image=false   
  6. description="Platform Architect and author (Linux, Embedded, Networking, AI)." 
  7. favourites_count=1   
  8. follow_request_sent=nil   
  9. followers_count=148   
  10. following=nil   
  11. friends_count=96   
  12. geo_enabled=true   
  13. id=16655901 id_str="16655901"   
  14. is_translator=false   
  15. lang="en"   
  16. listed_count=10   
  17. location="Colorado, USA"   
  18. name="M. Tim Jones"   
  19. notifications=nil   
  20. profile_background_color="1A1B1F"   
  21. profile_background_image_url="..." 
  22. profile_background_image_url_https="..."   
  23. profile_background_tile=false   
  24. profile_image_url="http://a0.twimg.com/profile_images/851508584/bio_mtjones_normal.JPG"   
  25. profile_image_url_https="..."   
  26. profile_link_color="2FC2EF"   
  27. profile_sidebar_border_color="181A1E" profile_sidebar_fill_color="252429"   
  28. profile_text_color="666666"   
  29. profile_use_background_image=true   
  30. protected=false   
  31. screen_name="MTimJones"   
  32. show_all_inline_media=false   
  33. status=<#Hashie::Mash   
  34. contributors=nil coordinates=nil   
  35. created_at="Sat Jul 02 02:03:24 +0000 2011"   
  36. favorited=false   
  37. geo=nil   
  38. id=86978247602094080 id_str="86978247602094080"   
  39. in_reply_to_screen_name="AnonymousIRC"   
  40. in_reply_to_status_id=nil in_reply_to_status_id_str=nil   
  41. in_reply_to_user_id=225663702 in_reply_to_user_id_str="225663702"   
  42. place=<#Hashie::Mash   
  43. attributes=<#Hashie::Mash>   
  44. bounding_box=<#Hashie::Mash   
  45. coordinates=[[[-105.178387, 40.12596],   
  46. [-105.034397, 40.12596],   
  47. [-105.034397, 40.203495],   
  48. [-105.178387, 40.203495]]]   
  49. type="Polygon" 
  50. >   
  51. country="United States" country_code="US"   
  52. full_name="Longmont, CO"   
  53. id="2736a5db074e8201"   
  54. name="Longmont" place_type="city"   
  55. url="http://api.twitter.com/1/geo/id/2736a5db074e8201.json" 
  56. >   
  57. retweet_count=0   
  58. retweeted=false   
  59. source="web"   
  60. text="@AnonymousIRC @anonymouSabu @LulzSec @atopiary @Anonakomis Practical reading   
  61. for future reference... LULZ \"Prison 101\" http://t.co/sf8jIH9" truncated=false 
  62. >  
  63. statuses_count=79   
  64. time_zone="Mountain Time (US & Canada)"   
  65. url="http://www.mtjones.com"   
  66. utc_offset=-25200   
  67. verified=false 
  68. >  
  69. => nil 
  70. irb(main):008:0>  

这就是快速展示部分的内容。现在,我们来研究一些简单的脚本,你可以在这些脚本中使用Ruby和Twitter API来收集和可视化数据。在这一过程中,你会了解到Twitter的一些概念,比如说身份验证和频率限制等。

挖掘Twitter数据

接下来的几节内容介绍几个通过Twitter API来收集和呈现可用数据的脚本,这些脚本重点在于其简易性,不过你可以通过扩展以及组合他们来创建新的功能。另外,本节内容还会提到Twitter gem API,这一API中有着更多可用的功能。

需要注意的很重要的一点是,在指定的时间内,Twitter API只允许客户做有限次的调用,这也就是Twitter的频率限制请求(现在是一小时不超过150次),这意味着经过某个次数的使用后,你会收到一个错误消息,并要求你在提交新的请求之前先做一段时间的等待。

用户信息

回想一下清单2中的每个Twitter用户的大量可用信息,只有在用户不受保护的情况下这些信息才是可访问的。我们来看一下如何以一种更便捷的方式来提取用户的信息并呈现出来。

清单3给出了一个(基于用户的界面显示名称)检索用户信息的简单的Ruby脚本,然后显示一些更有用的内容,在需要时使用Ruby方法to_s来把值转换成字符串。需要注意的是,首先用户是不受保护的,否则的话是不能访问到她/他的数据的。

清单3. 提取Twitter用户数据的简单脚本(user.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4.  
  5. screen_name = String.new ARGV[0]  
  6.  
  7. a_user = Twitter.user(screen_name)  
  8.  
  9. if a_user.protected != true 
  10.  
  11. puts "Username : " + a_user.screen_name.to_s  
  12. puts "Name : " + a_user.name  
  13. puts "Id : " + a_user.id_str  
  14. puts "Location : " + a_user.location  
  15. puts "User since : " + a_user.created_at.to_s  
  16. puts "Bio : " + a_user.description.to_s  
  17. puts "Followers : " + a_user.followers_count.to_s  
  18. puts "Friends : " + a_user.friends_count.to_s  
  19. puts "Listed Cnt : " + a_user.listed_count.to_s  
  20. puts "Tweet Cnt : " + a_user.statuses_count.to_s  
  21. puts "Geocoded : " + a_user.geo_enabled.to_s  
  22. puts "Language : " + a_user.lang  
  23. puts "URL : " + a_user.url.to_s  
  24. puts "Time Zone : " + a_user.time_zone  
  25. puts "Verified : " + a_user.verified.to_s  
  26. puts  
  27.  
  28. tweet = Twitter.user_timeline(screen_name).first  
  29.  
  30. puts "Tweet time : " + tweet.created_at  
  31. puts "Tweet ID : " + tweet.id.to_s  
  32. puts "Tweet text : " + tweet.text  
  33.  
  34. end 

若要调用这一脚本,需要确保其是可执行的(chmod +x user.rb),使用一个用户的名称来调用它。清单4显示了使用用户developerworks调用的结果,给出了用户的信息和当前状态(最后一条tweet消息)。这里要注意的是,Twitter把关注你的人定义为followers(粉丝);而把你关注的人称作friends(朋友)。

清单4. user.rb的输出例子

  1. $ ./user.rb developerworks  
  2. Username : developerworks  
  3. Name : developerworks  
  4. Id : 16362921  
  5. Location :   
  6. User since : Fri Sep 19 13:10:39 +0000 2008  
  7. Bio : IBM's premier Web site for Java, Android, Linux, Open Source, PHP, Social,   
  8. Cloud Computing, Google, jQuery, and Web developer educational resources  
  9. Followers : 48439  
  10. Friends : 46299  
  11. Listed Cnt : 3801  
  12. Tweet Cnt : 9831  
  13. Geocoded : false 
  14. Language : en  
  15. URL : http://bit.ly/EQ7te  
  16. Time Zone : Pacific Time (US & Canada)  
  17. Verified : false 
  18.  
  19. Tweet time : Sun Jul 17 01:04:46 +0000 2011  
  20. Tweet ID : 92399309022167040  
  21. Tweet text : dW Twitter is saving #IBM over $600K per month: will #Google+ add to that? >   
  22. http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app 

#p#

朋友的受欢迎情况

研究一下你的朋友(你关注的人),收集数据来了解一下他们的受欢迎程度。在这个例子中,收集朋友的数据并按照他们的粉丝数目来进行排序,这一简单的脚本如清单5所示。

在这一脚本中,在了解了你要(基于界面显示名称)分析的用户后,创建一个用户哈希表,Ruby哈希(或是相关的数组)是一种可以允许你定义存储键(而不是简单的数值索引)的数据结构。接着,通过Twitter的界面名称来索引这一哈希表,关联值则是用户的粉丝数目。这一过程简单地遍历你的朋友然后把他们的粉丝的数目放入哈希表中,接着(以降序)排列哈希表,然后把它放到输出中。

清单5. 关于朋友的受欢迎程度的脚本(friends.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4.  
  5. name = String.new ARGV[0]  
  6.  
  7. user = Hash.new 
  8.  
  9. # Iterate friends, hash their followers  
  10. Twitter.friends(name).users.each do |f|  
  11.  
  12. # Only iterate if we can see their followers  
  13. if (f.protected.to_s != "true")   
  14. user[f.screen_name.to_s] = f.followers_count   
  15. end 
  16.  
  17. end 
  18.  
  19. user.sort_by {|k,v| -v}.each { |user, count| puts "#{user}, #{count}" } 

清单5中的朋友脚本的一个例子输出如清单6所示。我删减了输出内容以节省空间,不过你可以看到,在我的直接网络中,ReadWriteWeb(RWW)和Playstation是很受欢迎的Twitter用户。

清单6. 清单5中的朋友脚本的界面输出

  1. $ ./friends.rb MTimJones  
  2. RWW, 1096862  
  3. PlayStation, 1026634  
  4. HarvardBiz, 541139  
  5. tedtalks, 526886  
  6. lifehacker, 146162  
  7. wandfc, 121683  
  8. AnonymousIRC, 117896  
  9. iTunesPodcasts, 82581  
  10. adultswim, 76188  
  11. forrester, 72945  
  12. googleresearch, 66318  
  13. Gartner_inc, 57468  
  14. developerworks, 48518 

我的粉丝来自哪里

回想一下清单2,Twitter提供了丰富的位置信息,有一个不限形式、用户定义的、可选用地理编码数据的位置字段。不过,用户设定的时区也可以为粉丝的实际位置提供线索。

在这一例子中,你要构建一个混搭应用程序来从Twitter粉丝中提取时区数据,然后使用Google Charts来可视化这一数据。Google Charts是一个非常有意思的项目,其允许你通过网络来构建各种各样不同类型的图表;把图表的类型和数据定义成HTTP请求,直接在浏览器中渲染作为响应的结果。若要安装用于Google Charts的Ruby gem,使用下面的命令行:

  1. $ gem install gchartrb 

清单7提供的脚本提取时区数据,接着构建Google Charts请求。首先,与之前的脚本不同,这一脚本需要你通过Twitter的身份验证。若要做到这一点,你需要注册一个Twitter应用,这会给你提供一组键值和令牌,这些令牌可以用在清单7的脚本中,这样才能够成功地取到数据。请参阅参考资料了解这一简单过程的详细情况。

按照相类似的模式,该脚本接受了一个界面名称,然后遍历该用户的粉丝,从当前粉丝中提取时区并把它存放在tweetlocation哈希表中。需要注意的是,你先要检测该键值是否已经存在于哈希表中,若是的话,增加该键值的计数。你还可以记住所有时区的个数,以用于后面的百分比的计算。

这一脚本的最后一部分内容是构造Google Pie Chart的URL,创建一个新的PieChart,指定一些选项(大小、标题和是否为3D等);接着,遍历时区哈希表,找出用于图表的时区串的数据(删去&符号)以及该时区计数与总数的百分比。

清单7.通过Twitter的时区来构建一个饼图(followers-location.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4. require 'google_chart' 
  5.  
  6. screen_name = String.new ARGV[0]  
  7.  
  8. tweetlocation = Hash.new 
  9. timezones = 0.0  
  10.  
  11. # Authenticate  
  12. Twitter.configure do |config|  
  13. config.consumer_key = '' 
  14. config.consumer_secret = '' 
  15. config.oauth_token = '  
  16. config.oauth_token_secret = '' 
  17. end 
  18.  
  19. # Iterate followers, hash their location  
  20. followers = Twitter.followers.users.each do |f|  
  21.  
  22. loc = f.time_zone.to_s  
  23.  
  24. if (loc.length > 0)  
  25.  
  26. if tweetlocation.has_key?(loc)  
  27. tweetlocation[loc] = tweetlocation[loc] + 1  
  28. else 
  29. tweetlocation[loc] = 1  
  30. end 
  31.  
  32. timezones = timezones + 1.0  
  33.  
  34. end 
  35.  
  36. end 
  37.  
  38. # Create a pie chart  
  39. GoogleChart::PieChart.new('650x350', "Time Zones", false ) do |pc|  
  40.  
  41. tweetlocation.each do |loc,count|  
  42. pc.data loc.to_s.delete("&"), (count/timezones*100).round  
  43. end 
  44.  
  45. puts pc.to_url  
  46.  
  47. end 

若要执行清单7中的脚本,给它提供一个Twitter界面显示名称,然后把得出的URL拷贝并粘贴到浏览器中,清单8给出了这一过程及最终生成的URL。

清单8. 调用followers-location脚本(结果只是一行来的)

  1. $ ./followers-location.rb MTimJones  
  2. http://chart.apis.google.com/chart?chl=Seoul|Santiago|Paris|Mountain+Time+(US++Canada)|  
  3. Madrid|Central+Time+(US++Canada)|Warsaw|Kolkata|London|Pacific+Time+(US++Canada)|  
  4. New+Delhi|Pretoria|Quito|Dublin|Moscow|Istanbul|Taipei|Casablanca|Hawaii|Mumbai|  
  5. International+Date+Line+West|Tokyo|Ulaan+Bataar|Vienna|Osaka|Alaska|Chennai|Bern|  
  6. Brasilia|Eastern+Time+(US++Canada)|Rome|Perth|La+Paz  
  7. &chs=650x350&chtt=Time+Zones&chd=s:KDDyKcKDOcKDKDDDDDKDDKDDDDOKK9DDD&cht=p  
  8. $  

把清单8中的URL粘贴到浏览器中,你就可以得到图1所示的结果。

图1. Twitter粉丝位置分布的饼图

 

 

Twitter用户的行为

Twitter包含了大量的数据,你可以通过挖掘这些数据来了解用户行为的某些要素。两个简单例子将用来分析Twitter用户何时发布消息以及通过什么应用来发布消息,你可以使用下面两个简单的脚本来提取并可视化这些信息。

清单9给出的脚本遍历了某个特定用户的tweet消息(使用user_timeline方法),然后从每条tweet消息中提取出该tweet消息形成的具体时间,一个简单的哈希表被再次用来统计一周中每天的计数,然后以与前面的时区例子相类似的方式使用Google Charts生成一个柱状图。

清单9. 构建tweet发布时间的柱状图(tweet-days.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4. require "google_chart" 
  5.  
  6. screen_name = String.new ARGV[0]  
  7.  
  8. dayhash = Hash.new 
  9.  
  10. timeline = Twitter.user_timeline(screen_name, :count => 200 )  
  11. timeline.each do |t|  
  12.  
  13. tweetday = t.created_at.to_s[0..2]  
  14.  
  15. if dayhash.has_key?(tweetday)  
  16. dayhash[tweetday] = dayhash[tweetday] + 1  
  17. else 
  18. dayhash[tweetday] = 1  
  19. end 
  20.  
  21. end 
  22.  
  23. GoogleChart::BarChart.new('300x200', screen_name, :vertical, false) do |bc|  
  24. bc.data "Sunday", [dayhash["Sun"]], '00000f' 
  25. bc.data "Monday", [dayhash["Mon"]], '0000ff' 
  26. bc.data "Tuesday", [dayhash["Tue"]], '00ff00' 
  27. bc.data "Wednesday", [dayhash["Wed"]], '00ffff' 
  28. bc.data "Thursday", [dayhash["Thu"]], 'ff0000' 
  29. bc.data "Friday", [dayhash["Fri"]], 'ff00ff' 
  30. bc.data "Saturday", [dayhash["Sat"]], 'ffff00' 
  31. puts bc.to_url  
  32. end 

图2提供了使用developerWorks帐户来执行清单9中的tweet-days脚本的结果,正如所展示的那样,星期三是发布tweet消息最活跃的一条,最不活跃的是星期六和星期天。

图2. 比较每天的tweet活跃程度的柱状图

 

 

接下来的脚本确定特定用户通过哪一个来源发布tweet消息,你可以通过几种方式来发布消息,这一脚本并未对每一种都做了编码。如清单10所示,使用类似的模式来提取给定用户的时间轴上的内容,然后尝试着解码哈希表中的tweet消息的发出处。稍后这一哈希表会被用来创建一个简单的饼图,使用Google Charts来可视化数据。

清单10. 构建用户的tweet消息源的饼图(tweet-source.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4. require 'google_chart' 
  5.  
  6. screen_name = String.new ARGV[0]  
  7.  
  8. tweetsource = Hash.new 
  9.  
  10. timeline = Twitter.user_timeline(screen_name, :count => 200 )  
  11. timeline.each do |t|  
  12.  
  13. if (t.source.rindex('blackberry')) then 
  14. src = 'Blackberry' 
  15. elsif (t.source.rindex('snaptu')) then 
  16. src = 'Snaptu' 
  17. elsif (t.source.rindex('tweetmeme')) then 
  18. src = 'Tweetmeme' 
  19. elsif (t.source.rindex('android')) then 
  20. src = 'Android' 
  21. elsif (t.source.rindex('LinkedIn')) then 
  22. src = 'LinkedIn' 
  23. elsif (t.source.rindex('twitterfeed')) then 
  24. src = 'Twitterfeed' 
  25. elsif (t.source.rindex('twitter.com')) then 
  26. src = 'Twitter.com' 
  27. else 
  28. src = t.source  
  29. end 
  30.  
  31. if tweetsource.has_key?(src)  
  32. tweetsource[src] = tweetsource[src] + 1  
  33. else 
  34. tweetsource[src] = 1  
  35. end 
  36.  
  37. end 
  38.  
  39. GoogleChart::PieChart.new('320x200', "Tweet Source", false) do |pc|  
  40.  
  41. tweetsource.each do|source,count|  
  42. pc.data source.to_s, count  
  43. end 
  44.  
  45. puts "\nPie Chart" 
  46. puts pc.to_url  
  47. end 

图3提供了一个可视化图表,显示了一组Twitter用户感兴趣使用的tweet消息发布源,传统的Twitter网站最常用到,随后是移动电话应用。

图3. Twitter用户的tweet消息发布源的饼图 

 

 

#p#

反映粉丝情况的图表

Twitter是一个庞大的用户网络,这些用户形成了一个网络图。正如你通过脚本所看到的那样,遍历你的联系人很容易,接着遍历他们的联系人也很容易。即使只是在这一级别上,这样做也已建立起一张大图的基本面。

为了可视化图形,我选择使用图形可视化软件GraphViz。在Ubuntu上,使用下面的命令行,你可以很容易就安装好这一工具:

  1. $ sudo apt-get install graphviz 

清单11中的脚本遍历了用户的粉丝,然后再遍历这些粉丝他们的粉丝。这一模式中的唯一真正不同之处在于 GraphViz的DOT格式文件的构造,GraphViz使用简单的脚本格式来定义图形,这些图形作为你列举的Twitter用户的组成部分出现。如清单所展示的那样,可以简单地通过指定节点的关系来定义图形。

清单11. 可视化Twitter粉丝图(followers-graph.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4. require 'google_chart' 
  5.  
  6. screen_name = String.new ARGV[0]  
  7.  
  8. tweetlocation = Hash.new 
  9.  
  10. # Authenticate  
  11. Twitter.configure do |config|  
  12. config.consumer_key = '' 
  13. config.consumer_secret = '' 
  14. config.oauth_token = '' 
  15. config.oauth_token_secret = '' 
  16. end 
  17.  
  18. my_file = File.new("graph.dot", "w")  
  19.  
  20. my_file.puts "graph followers {" 
  21. my_file.puts " node [ fontname=Arial, fontsize=6 ];" 
  22.  
  23. # Iterate followers, hash their location  
  24. followers = Twitter.followers(screen_name, :count=>10 ).users.each do |f|  
  25.  
  26. # Only iterate if we can see their followers  
  27. if (f.protected.to_s != "true")  
  28.  
  29. my_file.puts " \"" + screen_name + "\" -- \"" + f.screen_name.to_s + "\"" 
  30.  
  31. followers2 = Twitter.followers(f.screen_name, :count =>10 ).users.each do |f2|  
  32.  
  33. my_file.puts " \"" + f.screen_name.to_s + "\" -- \"" +  
  34. f2.screen_name.to_s + "\"" 
  35.  
  36. end 
  37.  
  38. end 
  39.  
  40. end 
  41.  
  42. my_file.puts "}" 

在某个用户上执行清单11的脚本,得出的结果放在一个dot文件中,接着使用GraphViz来生成图像。首先调用Ruby脚本来收集图形数据(存储成graph.dot);接着使用GraphViz来生成图像(这里用的是circo,该工具指定了一种圆形的布局)。生成图像的过程定义如下:

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

最终得出的图像如图4所示。需要注意的是,由于这一Twitter图表可能会很大,因为我们通过尽量减少要列举的用户及其粉丝的数目来限制图表的规模(通过清单11中的:count选项)。

图4. Twitter粉丝图例子(极端情况的子集)

 

 

位置信息

在功能启用的情况下,Twitter收集你和你发出tweet消息的地理位置数据。这些由经度和纬度信息组成的数据能够用来精确地定位用户的位置或者是tweet消息从何处发出。此外,把搜索和这些消息结合在一起,你就可以根据已定义的位置或是你所处的位置来识别出某些地方或是某些人。

并非所有的用户或是tweet消息都启用了地理位置功能(出于隐私原因),但这一信息是所有Twitter体验当中的一个非常吸引人的方面。让我们来看一个脚本,该脚本允许你可视化地理定位数据,还有另一个脚本允许你使用这一数据来进行搜索。

第一个脚本(清单12所示)抓取用户的经度和纬度数据(回想一下清单2中的边界框(bounding box)),尽管边界框是一个定义代表了用户区域的多边形,但我做了简化,只用了这一区域中的一个点。有了这一数据,我在一个简单的HMTL文件中生成了一个简单的JavaScript函数,这一JavaScript代码与Google Maps接口,给出了这一位置的一个俯视地图(给出提取自Twitter用户的经度和纬度数据)。

清单12. 构造用户地图的Ruby脚本(where-am-i.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4.  
  5. Twitter.configure do |config|  
  6. config.consumer_key = '<consumer_key>  
  7. config.consumer_secret = '<consumer_secret>  
  8. config.oauth_token = '<oauth_token>  
  9. config.oauth_token_secret = '<token_secret>  
  10. end 
  11.  
  12. screen_name = String.new ARGV[0]  
  13.  
  14. a_user = Twitter.user(screen_name)  
  15.  
  16. if a_user.geo_enabled == true 
  17.  
  18. long = a_user.status.place.bounding_box.coordinates[0][0][0];  
  19. lat = a_user.status.place.bounding_box.coordinates[0][0][1];  
  20.  
  21. my_file = File.new("test.html", "w")  
  22.  
  23. my_file.puts "<!DOCTYPE html>  
  24. my_file.puts "<html>< head>  
  25. my_file.puts "<meta name=\"viewport\" content=\"initial-scale=1.0, user-scalable=no\"/>" 
  26. my_file.puts "<style type=\"text/css\">" 
  27. my_file.puts "html { height: 100% }" 
  28. my_file.puts "body { height: 100%; margin: 0px; padding: 0px }" 
  29. my_file.puts "#map_canvas { height: 100% }" 
  30. my_file.puts "</style>" 
  31. my_file.puts "< script type=\"text/javascript\"" 
  32. my_file.puts "src=\"http://maps.google.com/maps/api/js?sensor=false\">" 
  33. my_file.puts "</script>" 
  34. my_file.puts "<script type=\"text/javascript\">" 
  35. my_file.puts "function initialize() {" 
  36. my_file.puts "var latlng = new google.maps.LatLng(" + lat.to_s + ", " + long.to_s + ");" 
  37. my_file.puts "var myOptions = {" 
  38. my_file.puts "zoom: 12," 
  39. my_file.puts "center: latlng," 
  40. my_file.puts "mapTypeId: google.maps.MapTypeId.HYBRID" 
  41. my_file.puts "};" 
  42. my_file.puts "var map = new google.maps.Map(document.getElementById(\"map_canvas\")," 
  43. my_file.puts "myOptions);" 
  44. my_file.puts "}" 
  45. my_file.puts "</script>" 
  46. my_file.puts "</head>" 
  47. my_file.puts "<body onload=\"initialize()\">" 
  48. my_file.puts "<div id=\"map_canvas\" style=\"width:100%; height:100%\"></div>" 
  49. my_file.puts "</body>" 
  50. my_file.puts "</html>" 
  51.  
  52. else 
  53.  
  54. puts "no geolocation data available." 
  55.  
  56. end 

清单12中的脚本执行起来很简单:

  1. $ ./where-am-i.rb MTimJones 

最终得出的HTML文件可以通过浏览器来渲染,像这样:

  1. $ firefox test.html 

如果没有可用的位置信息的话,这一脚本就会执行失败;不过如果执行成功的话,就会生成一个HTML文件,浏览器可以读入该文件来渲染出地图。图5给出了最终生成的地图图像,其显示了美国科罗拉多州北部Front Range的部分地区。

图5. 清单12中的脚本渲染图像的例子

 

 

有了地理位置,你还可以通过搜Twitter来识别出与某个特定位置相关联的Twitter用户和tweet消息。Twitter搜索API允许使用地理编码信息来限定搜索结果。下面清单13中的例子提取用户的经度和纬度,然后使用这些数据获取该位置方圆5公里之内的所有tweet消息。

清单13. 使用经度和纬度数据来搜索局部地区的tweet消息(tweets-local.rb)

  1. #!/usr/bin/env ruby  
  2. require "rubygems" 
  3. require "twitter" 
  4.  
  5. Twitter.configure do |config|  
  6. config.consumer_key = '' 
  7. config.consumer_secret = '' 
  8. config.oauth_token = '' 
  9. config.oauth_token_secret = '' 
  10. end 
  11.  
  12. screen_name = String.new ARGV[0]  
  13.  
  14. a_user = Twitter.user(screen_name)  
  15.  
  16. if a_user.geo_enabled == true 
  17.  
  18. long = a_user.status.place.bounding_box.coordinates[0][0][0]  
  19. lat = a_user.status.place.bounding_box.coordinates[0][0][1]  
  20.  
  21. Array tweets = Twitter::Search.new.geocode(lat, long, "5mi").fetch  
  22.  
  23. tweets.each do |t|  
  24.  
  25. puts t.from_user + " | " + t.text  
  26.  
  27. end 
  28.  
  29. end 

清单13中的脚本的执行结果显示在清单14中,这是指定位置范围内的Twitter用户发布的tweet消息的一个子集。

清单14. 查询我所在位置方圆5公里之内的这一局部地区的tweet消息。

  1. $ ./tweets-local.rb MTimJones  
  2. Breesesummer | @DaltonOls did he answer u  
  3. LongmontRadMon | 60 CPM, 0.4872 uSv/h, 0.6368 uSv/h, 2 time(s) over natural radiation  
  4. graelston | on every street there is a memory; a time and place we can never be again.  
  5. Breesesummer | #I'minafight with @DaltonOls to see who will marry @TheCodySimpson I will   
  6. marry him!!! :/  
  7. _JennieJune_ | ok I'm done, goodnight everyone!  
  8. Breesesummer | @DaltonOls same  
  9. _JennieJune_ | @sylquejr sleep well!  
  10. Breesesummer | @DaltonOls ok let's see what he says  
  11. LongmontRadMon | 90 CPM, 0.7308 uSv/h, 0.7864 uSv/h, 2 time(s) over natural radiation  
  12. Breesesummer | @TheCodySimpson would u marry me or @DaltonOls 
  13. natcapsolutions | RT hlovins: The scientific rebuttal to the silly Forbes release this   
  14. morning: Misdiagnosis of Surface Temperatu... http://bit.ly/nRpLJl  

继续深入

本文给出了一些简单的脚本,这些脚本使用Ruby语言来从Twitter中提取数据,文章的重点放在用来说明一些基本概念的脚本的开发和演示方面,但我们能做的远不止于此。例如,你还可以使用API来浏览你的朋友的网络,并找出你感兴趣的最受欢迎的Twitter用户。另一个吸引人的方面是对tweet消息本身进行挖掘,使用地理位置数据来了解基于位置的行为或是事件(比如说流感暴发)。本文只是做了一个浅显的介绍,请在下面随意发表你的评论,附上你自己的各种混搭程序。Ruby和Twitter gem把为数据挖掘需要开发有用的混搭应用程序和仪表盘工具的工作变得很容易。

关于作者

 

M. Tim Jones是一位嵌入式固件架构师,也是Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming (现在是第二版)、AI Application Programming (第二版)和BSD Sockets Programming from a Multilanguage Perspective等书的作者。他的工程背景涉及广泛,从同步宇宙飞船的内核开发到嵌入式系统架构和网络协议开发都有。Tim是Intel的一位平台架构师,也是一位作家,居住在科罗拉多州的 Longmont。

原文:http://select.yeeyan.org/view/213582/223628

【编辑推荐】