Rails / SNSの人気順で記事を並び替えたい（カウント数を取得して保存） Facebook / Twitter / Pocket / はてな / Google+ 

- - - - - - - - - - - 
ブログを更新しました。<a href="http://workabroad.jp/posts/2132">元の記事はコチラ</a>
- - - - - - - - - - - 
人気の記事を表示させたいなぁと思ったけど、いざやるとなると指標を何にしていいのかわからない。
Facebookでシェアが多いものもあれば、はてなブックマークが多いものもある。

だから全部にしてみた。

<!-- extract -->
　　
![social](http://image.workabroad.jp/upload/2015/247/workabroad.jpg)

取得するのは次の5種類。

- Facebook いいね数（たぶんシェア数も？）
- Twitter ツイート数
- Pocket
- Hatena はてなブックマーク件数
- Google plus +1数


###コード

rakeタスクのつもりだったけど、
コントローラーでも使うかなーと思ったのでモジュールにしました。
[Nokogiri](http://www.nokogiri.org/)つかうので ```bundle install``` しておいてください。
　　
なんだかDRYじゃない感じですが取得の仕方がそれぞれ違ってくるので、
ばらばらのメソッドにしておいた方が楽だと思います。

```ruby

module SnsCount

	require 'open-uri'
	require 'nokogiri'

	def pocket_count(url)
		api = "http://widgets.getpocket.com/v1/button?v=1&count=horizontal&url="
		content = get_content(api + url)
		return 0 if content.blank?

		content.xpath("//*[@id='cnt']").try(:text)
	end


	def twitter_count(url)
		api = "http://urls.api.twitter.com/1/urls/count.json?url="
		html = open(api + url).read
		return 0 if html.blank?

		json = JSON.parse(html)
		json["count"]
	end


	def hatena_count(url)
		api = "http://api.b.st-hatena.com/entry.count?url="
		html = open(api + url).read
		return 0 if html.blank?

		html
	end


	def facebook_count(url)
		api = "http://graph.facebook.com/"
		html = open(api + url).read
		return 0 if html.blank?

		json = JSON.parse(html)
		json["shares"]
	end


	def google_count(url)
		api = "https://apis.google.com/_/+1/fastbutton?url="
		content = get_content(api + url)
		return 0 if content.blank?
		
		content.xpath("//*[@id='aggregateCount']").try(:text)
	end


	def get_content(url)
		html = open(url).read
		Nokogiri::HTML(html, nil, 'utf-8')
	end

end

```

　　

```
#Pocket
http://widgets.getpocket.com/v1/button?v=1&count=horizontal&url=

#Twitter
http://urls.api.twitter.com/1/urls/count.json?url=

#はてな
http://api.b.st-hatena.com/entry.count?url=

#Facebook
http://graph.facebook.com/

#Google+
https://apis.google.com/_/+1/fastbutton?url=
```

それぞれカウント取得したいページのURLをつけて叩けば何かしら返ってくるので、
あとはそこから数字を抜き取っているだけ。
　　

###コントローラーで使ったり

app/controllers/posts_controller.rb

```ruby
class PostsController < ApplicationController

	require 'sns_count'
	include SnsCount

	def show
	  @post = Post.find(params[:id])
	  @pocket = pocket_count("http://www.workabroad.jp/posts/#{post.id}")
	end

end
```


###rakeタスクで使ったり

今回やったのはこっち。
毎日1回このタスクを走らせればいいかなと思ってる。

　　
カウント数をDBに保存するので、それ用のカラムが必要です。
migrate/20150203140953_add_sns_count_to_post.rb

```rake db:migrate``` してね。

```
class AddSnsCountToPost < ActiveRecord::Migration
  def change
    add_column :posts, :pocket_count, :integer, default: 0
    add_column :posts, :twitter_count, :integer, default: 0
    add_column :posts, :hatena_count, :integer, default: 0
    add_column :posts, :facebook_count, :integer, default: 0
    add_column :posts, :google_count, :integer, default: 0
  end
end

```

　　
lib/task/get_sns_count.rake

```ruby
namespace :sns do
	
	desc "Get SNS count and store them in DB."

	task get_sns_count: :environment do
		get_sns_count
	end

	def get_sns_count
		sns = SnsController.new

		Post.all.where(published: true).each do |post|
			# www(sub domain) is required.
			# Otherwise twitter/google/hatena won't return the count properly.
			base_url = "http://www.workabroad.jp/posts/"
			url = base_url + post.id.to_s
			puts "Getting SNS count: #{url}"
			post.pocket_count 	= sns.pocket_count(url).to_i
			post.twitter_count 	= sns.twitter_count(url).to_i
			post.hatena_count 	= sns.hatena_count(url).to_i
			post.facebook_count = sns.facebook_count(url).to_i
			post.google_count 	= sns.google_count(url).to_i

			puts 	"p:#{post.pocket_count} | t:#{post.twitter_count} | h:#{post.hatena_count} | " +
						"f:#{post.facebook_count} | g:#{post.google_count}"
			if post.changed?
				post.save
				puts "Saved!"
			else
				puts "Not Changed."
			end
			sleep(1)
		end
	end

end

```


　　
このブログはルートドメインへのアクセスをwww.に転送しています。
→詳しくはこちら。
[heroku で 独自ドメインを使う際の最善策を考えた | Workabroad.jp](http://www.workabroad.jp/posts/2002)

そんな場合は、
```http://workabroad.jp/:id``` ではなく
```http://www.workabroad.jp/:id``` とURLを指定しないとうまくカウントが返ってきません。
違うページだと判断されているんだと思う。

ここハマった。
× Twitter / Google / はてな
○ FacebookとPocketはルートでもwww.でも同一とみなしてくれる。



###表示

あとはSNSカウントの合計が多い順に記事を並べれば完成。
リアルタイムではないけど、ランキング用途には十分だと思います。

app/models/post.rb

```ruby
class Post < ActiveRecord::Base

	scope :by_sns, -> { unscoped.order('pocket_count + twitter_count + hatena_count + facebook_count + google_count DESC') }

end
```

　　
app/controllers/posts_controller.rb

```ruby
class PostsController < ApplicationController

	def index
	  @popular_posts = Post.by_sns.limit(10)
	end

end
```

　　
以上です。
しかし一番人気のある記事がドラゴンボールの曲だとは…。
なんだか拍子抜け。。

###参考

[はてなブックマーク件数取得API - Hatena Developer Center](http://developer.hatena.ne.jp/ja/documents/bookmark/apis/getcount)
[Google+のカウント数を取得するために色々試した結果がこちら](http://hayashikejinan.com/webwork/sns/358/)
　　

 → <a href="http://workabroad.jp/posts/2132">http://workabroad.jp/posts/2132</a>