Rails / SNSの人気順で記事を並び替えたい(カウント数を取得して保存) Facebook / Twitter / Pocket / はてな / Google+
人気の記事を表示させたいなぁと思ったけど、いざやるとなると指標を何にしていいのかわからない。
Facebookでシェアが多いものもあれば、はてなブックマークが多いものもある。
だから全部にしてみた。
取得するのは次の5種類。
- Facebook いいね数(たぶんシェア数も?)
- Twitter ツイート数
- Hatena はてなブックマーク件数
- Google plus +1数
コード
rakeタスクのつもりだったけど、
コントローラーでも使うかなーと思ったのでモジュールにしました。
Nokogiriつかうので bundle install
しておいてください。
なんだかDRYじゃない感じですが取得の仕方がそれぞれ違ってくるので、
ばらばらのメソッドにしておいた方が楽だと思います。
ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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
1
2
3
4
5
6
7
8
9
10
11
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
してね。
1
2
3
4
5
6
7
8
9
10
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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://workabroad.jp/:id
ではなく
http://www.workabroad.jp/:id
とURLを指定しないとうまくカウントが返ってきません。
違うページだと判断されているんだと思う。
ここハマった。
× Twitter / Google / はてな
○ FacebookとPocketはルートでもwww.でも同一とみなしてくれる。
表示
あとはSNSカウントの合計が多い順に記事を並べれば完成。
リアルタイムではないけど、ランキング用途には十分だと思います。
app/models/post.rb
ruby
1
2
3
4
5
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
1
2
3
4
5
6
7
class PostsController < ApplicationController
def index
@popular_posts = Post.by_sns.limit(10)
end
end
以上です。
しかし一番人気のある記事がドラゴンボールの曲だとは…。
なんだか拍子抜け。。
参考
はてなブックマーク件数取得API - Hatena Developer Center
Google+のカウント数を取得するために色々試した結果がこちら