Rubyで乃木坂46流行語大賞を集計する
はじめに
乃木坂46のメンバーである伊藤かりんさんが毎年年末になると乃木坂46流行語大賞というものを発表していて、その投票をかりんさんのブログのコメントにて受け付けています。
2018年の当該ポストはこちらです。
開票作業は毎年かりんさん本人が「正」の字を書いて集計してるらしく、大変そうなのでプログラミングの力を借りましょう。
投票方法の詳細は上記のブログに委ねますが、コメントの名前欄に投票したい言葉を記入するルールになったので、スクレイピングしやすそうです。
すべてのコードはGistに置いておきます。
流れ
- スクレイピングをしてデータを取得→ファイルに保存
- 保存したファイルのデータを取得してそれを加工していく
というやり方でやっていきます。
まずはスクレイピング
ブログにアクセスしてコメントの総数と「次へ」リンクを押したときのURLの変化を見てみましょう。
URLの一部の数字が50ずつ増えているのがわかると思います。
例えばコメントの総数が1000だったら以下のようにすると50ずつの配列が得られます。
max = 1000
page = 0.step(max, 50).to_a
p page # => [0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000]
Nokogiriを使う
上記の配列を使いそれぞれのURLにアクセスして該当する文字列を取得します。
require "nokogiri"
require "open-uri"
words = []
page.each do |p|
url = "http://blog.nogizaka46.com/karin.itou/2018/11/047843.php?cp=#{p}#comments"
doc = Nokogiri::HTML(URI.open(url, "User-Agent" => "firefox"))
words << doc.css(".vcard").map { |i| i.text.gsub(/\s+/, "") }
end
Nokogiriを使うのでインストールしておきましょう。
ちなみにUser-Agent
を適当に指定しないとopen_http': 403 Forbidden (OpenURI::HTTPError))'
エラーがでてしまいました。
取得したい文字列はvcard
classを指定すれば良さそうです。gsub
では余分なスペースを取り除いています。
取得した文字列をwords
配列に追加しています。
ファイルに書き出す
words
配列を適当なファイルに書き出します。
File.open("word.txt", "w") do |f|
words.each { |w| f.puts(w) }
end
わざわざファイルに書き出さなくてもこの後の処理はできますが、デバッグなどをしながら外部サイトに何度もアクセスするのは良くないのでこのような形にしました。
プログラムを実行
ここまでをひとつのファイルとして(ここではnogiblog.rb
とする)保存し、Rubyプログラムを実行してみましょう。
ruby nogiblog.rb
word.txt
が作成され、データが保存できているはずです。
集計する
取得したデータをもとに集計していきます。
今回は投票数の多い言葉トップ20を出してみましょう。buzzword.rb
というファイルを作成します。
ファイルを読み込む
words.txt
の中身をwords
配列にします。
words = []
File.open("word.txt", "r") do |f|
words = f.readlines
end
同じ文字列をカウント
each_with_object
とハッシュを使うことでという形で配列内の要素数を得ることができます。
同じ文字列があればカウントが増えていきます。
chomp!
は改行文字を取り除いています。
word_count = words.each_with_object(Hash.new(0)) do |word, hash|
hash[word.chomp!] += 1
end
p word_count # => {"単語1"=>1, "単語2"=>1, "単語3"=>1 ... }`
トップ20
word_count
ハッシュをハッシュの値であるカウントした数字でソートします。
-
をつけることで逆順になります。さらにtake
を使って20個の要素を先頭から取り出します。
top = word_count.sort_by { |_, count| -count }.take(20)
似たような文字列を集める
同じ事柄を表現した場合でも投票する人によって書き方が違うことがあります。
例えばシンクロニシティ
,シンクロ
,シンクロライブ
といったようにバラけてしまうので、それらを同じとしてカウントするかどうかは置いといて、似たような文字列は隣同士に置いてわかりやすくしたいと思います。
今回はトップ20の文字列の頭文字三文字を使って、もしword_count
ハッシュのキーがその三文字で始まっていたらその文字列を表示する、というやり方でやってみます。
正直もっと他に良い方法がありそうなので誰か教えて下さい🤔
initial = top.map { |t| t[0].slice(0..2) }.uniq
initial.each do |i|
word_count.each do |word, count|
if word.start_with?(i)
puts [count.to_s.rjust(4), word].join(" | ")
end
end
end
initial
では頭文字三文字を取得しています。uniq
は重複を避けるためです。
rjust
やjoin
は出力を整形して見やすくするために使っています。
プログラムを実行
以上をbuzzword.rb
に書いてプログラムを実行してみましょう。
この記事を書いている時点(2018年11月21日)で4000以上の投票がされていて結果は以下のようになっています。
結果を知りたくなければ、これより下を見ないことをおすすめします😏
ruby buzzword.rb
620 | ヤラカシタヤラカシタ
39 | ヤラカシタ
95 | ヤラカシタ、ヤラカシタ
16 | ヤラカシタ ヤラカシタ
1 | ヤラカシタヤラカシタ(棒読み)
2 | ヤラカシタやらかした
2 | ヤラカシタ、ヤラカシタ
1 | ヤラカシタ、ヤラカシタ。
301 | 卒業
38 | 卒業ラッシュ
1 | 卒業(ラッシュ)
1 | 卒業メンバーロス
1 | 卒業です
1 | 卒業。
1 | 卒業発表
183 | 乃木撮
1 | 乃木撮か、でんちゃん!の棒読みかな?
1 | 乃木撮!!!
1 | 乃木撮り
...
これで乃木坂46流行語大賞を集計することができました。
付け足し
今回はトップ20をだしてみましたが、take
のところの数字を増やしたり、あるいはtake
自体を削除して実行してみると面白いかもしれません。
もし似たような文字列も同一のものとみなすなら順位の変動もあり得ます。
Author And Source
この問題について(Rubyで乃木坂46流行語大賞を集計する), 我々は、より多くの情報をここで見つけました https://qiita.com/hachy/items/2e4fa0736f98361df5fe著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .