はじめに
昨日、Gunosyは多くのユーザに同じ記事を配信しているのか検証してみたという記事を書いた。
ランダムにピックアップしたGunosyユーザの特定の日の配信記事リストから、記事URLを使って重複をカウントし、本当に重複記事数が著しく多かったり、ユーザ間の重複率が批判の通りなのかを検証したものだ。
こちらでは、技術ブログなので検証に使ったコードを公開して簡単に解説しよう。
技術的な方針
今回は、素早く分析するためにフレームワークなどは使わず、捨てる前提のスクリプトを以下の方針で書いた。
- Google Custom Search APIによりGunosyの公開ユーザ名を取得
- (1)で取得したユーザに特定の日(今回は5/5)に配信された記事のURLとタイトルを取得
- ユーザ間の重複数を記事毎に数えてファイルに書き出す
事前準備
Google Custom Search APIにより、gunosy.com内の公開ユーザ名をリストアップする。
ここはコードにしても良かったが、Google APIの制約上、上位100件までしか取得できないことが分かっていたため、ブラウザからAPIにパラメータを変えて10回アクセスし、それぞれの結果を1.jsonから10.jsonとしてローカルに保存した。(これを後述のimport.rbから読み込む)
Google Custom Search APIの使い方は、Getting Started with the API – Custom Search — Google Developersに書いてある順番通りに作業すれば使えるようになると思うので試してみてほしい。
以下のINSERT-YOUR-KEYの部分を自身のKEYに変えてリクエストすると試せると思う。
なお、2ページ目以降を取得する際に変更するパラメータはstartだ。
https://www.googleapis.com/customsearch/v1?key=INSERT-YOUR-KEY&cx=016434714500670441010:kxhgwdwqsza&q=site:gunosy.com&start=1
今回は、1.jsonから10.jsonを含むファイル群を後述のRubyファイルと共にGithubに上げてあるので、試す人はそちらを使うと良いと思う。
noriaki/validate-gunosy at 20130506 · GitHub
モデル (ORM)
データベースには、MongoDBを利用した。ローカルにMongoDBがインストールされていて、ポート27017(デフォルト設定)で起動していることを前提にしている。
user.rb
class User
include Mongoid::Document
field :name
has_many :recommendeds, dependent: :destroy
def url(date=Date.yesterday)
"http://gunosy.com/#{name}/#{date.strftime("%Y/%m/%d")}"
end
end
ユーザ名をnameカラムとして保持する。
また、urlメソッドにより、特定の日にユーザへ配信されたGunosy上の記事一覧ページURLを作っている。
article.rb
class Article
include Mongoid::Document
field :url, type: String
field :title, type: String
has_many :recommendeds, dependent: :destroy
has_many :pickings, dependent: :destroy
end
こちらもUserモデルと同様の形式で、配信された記事URLとタイトルを保持している。
recommended.rb
これは、UserモデルとArticleモデルを多対多でつなぐためのモデルで、両モデルのIDだけを保持している。本来のKVSの使い方とは違うかもしれないが、自分としては分かりやすいので良くこういった書き方をする。
class Recommended
include Mongoid::Document
field :user_id
field :article_id
belongs_to :user
belongs_to :article
end
事前準備したデータを読み込む (Import)
import.rb
このファイルは以下のように単体で実行すると、既存DBをクリアして事前準備したデータを読み込む。
% ruby import.rb
require 'json'
require 'open-uri'
require 'nokogiri'
require 'mongoid'
require 'csv'
require 'kconv'
$LOAD_PATH.push '.'
require 'user'
require 'article'
require 'recommended'
Mongoid.configure do |conf|
conf.master = Mongo::Connection.new('localhost', 27017).db('validate_gunosy')
end
if __FILE__ == $0
Article.destroy_all
User.destroy_all
search_results = (1..10).map{ |i|
JSON.parse(File.open("g/#{i}.json").read)['items'].map do |t|
t['link']
end
}.flatten
search_results.each do |l|
name = l[/[^g]\/([^\/]*?)\/?$/,1]
if name && !%w(gunosy.com signup login iphone).include?(name)
User.create name: name
end
end
links = User.all.each do |user|
Nokogiri(
(open(user.url(Date.parse('2013/5/5'))) rescue StringIO.new).read
).css('article h1').each do |e|
article = Article.find_or_initialize_by url: e.parent['href']
article.title = e.text.strip
article.save
Recommended.create user_id: user.id, article_id: article.id
end
end
end
そんなに複雑なことはやっていないが、GitHubにも上げた先述の1~10.jsonを順番に読み込み、ユーザ名を抽出して、それぞれの5/5に配信された記事一覧をスクレイピングして取得している。
irb -r”./import”
最後にirbを以下のようにimport.rbを読み込んで起動し、各種検証用に数字を確認した。
% irb -r"./import"
TSVファイルへ結果を書き出す
上記の通り起動したirb上で以下のコードを実行すると、articles.tsvにデータが出力される。Excel以外で見るときなどは、.tosjis部分を削除すれば文字コードがUTF-8になるはずだ。
CSV.open('articles.tsv', 'w', col_sep: "\t") do |row|
Article.all.each do |a|
row << [a.url,a.title.tosjis,a.recommendeds.count]
end
end
なお、最後になったがRubyのバージョンは以下である。
% ruby -v
ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-linux]
さて、はてブのホットエントリーデータも入手できたことなので、次回はさらにはてブとGunosyが似ているという点についても調べてみよう。
スポンサード