コード解説:Gunosyは多くのユーザに同じ記事を配信しているのか検証してみた

Gunosyは多くのユーザに同じ記事を配信しているのか検証してみた

はじめに

昨日、Gunosyは多くのユーザに同じ記事を配信しているのか検証してみたという記事を書いた。

ランダムにピックアップしたGunosyユーザの特定の日の配信記事リストから、記事URLを使って重複をカウントし、本当に重複記事数が著しく多かったり、ユーザ間の重複率が批判の通りなのかを検証したものだ。

こちらでは、技術ブログなので検証に使ったコードを公開して簡単に解説しよう。

技術的な方針

今回は、素早く分析するためにフレームワークなどは使わず、捨てる前提のスクリプトを以下の方針で書いた。

  1. Google Custom Search APIによりGunosyの公開ユーザ名を取得
  2. (1)で取得したユーザに特定の日(今回は5/5)に配信された記事のURLとタイトルを取得
  3. ユーザ間の重複数を記事毎に数えてファイルに書き出す

事前準備

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が似ているという点についても調べてみよう。

スポンサード

Lokkaをgit cloneしてからpull requestするまで

Lokkaをgithubでforkする

githubのLokkaリポジトリの右上にある「Fork」ボタンをクリック。

forkしたLokkaを自分のlocal環境へclone

% git clone git@github.com:noriaki/lokka.git lokka-noriaki

変更用のbranchを作りcodeに変更を加える

% cd lokka-noriaki
% git co -b spike_new_feature
% emacs lib/lokka/entry.rb # codeを修正・追加
% emacs spec/post_spec.rb # ちゃんとtestも書く

gemをインストールしてrspecによるunit testを走らせる

localでテストするだけなら、最小構成は以下でインストールできる。

% bundle install --without=production:development:mysql:postgresql
% rake spec

testをパスしたらcommitして自分のremoteリポジトリへpush

% git ci -a -m "added new feature"
% git push origin spike_new_feature

いざpull request

  1. 自分のremoteリポジトリへ行って
  2. branchを今回の作業で作った変更用branch(
    spike_new_feature

    )へ変更

  3. 右上にある「pull request」ボタンを押す

※参考:GitHubへpull requestする際のベストプラクティス