ブログお引越ししました。
世界の切りとり方
5秒後に移動します…

青空文庫の閲覧数ランキングを集計してみた

RubyのWebスクレイピングライブラリNokogiriを使って、
青空文庫が月ごとに発表している閲覧数ランキングを集計してみました。

ランキングページには、
2009年1月〜現在まで、各月閲覧ファイル種別(XHTML版,TEXT版)に2つずつリンクが並んでいるので、
そのページを元に各ランキングをCSVにして、集計という手順で処理しました。

上位10作品はこんな感じでした。漱石多いなあ。

"1","こころ","夏目 漱石","473813"
"2","銀河鉄道の夜","宮沢 賢治","440443"
"3","人間失格","太宰 治","384592"
"4","吾輩は猫である","夏目 漱石","362631"
"5","坊っちゃん","夏目 漱石","348445"
"6","ドグラ・マグラ","夢野 久作","303008"
"7","羅生門","芥川 竜之介","280554"
"8","蟹工船","小林 多喜二","239553"
"9","注文の多い料理店","宮沢 賢治","217020"
"10","学問のすすめ","福沢 諭吉","209673"

ソースはこちら。
年月指定やファイル種別指定できるようにした方がよかったな。

require 'nokogiri'
require 'kconv'
require 'open-uri'

class Aozora
    def csv_nizer(item="", isEOL=false, brachets='"', separater=',' )
        brachets + item + brachets + (isEOL ? "\n" : separater )
    end 

    def parseHtmlFileAndEncodeUtf8(url)
        Nokogiri::HTML.parse((open(url, "r:binary").read.toutf8.encode("UTF-8")))
    end 
    
    RankingListURL="http://www.aozora.gr.jp/access_ranking/"
    CSV = ".csv"

    def getRankingAsCsv(url, outputPath, filename:"out.csv")
        begin
            path = File.directory?(outputPath) ? File.expand_path(outputPath) : Dir.pwd
            doc = parseHtmlFileAndEncodeUtf8(url)
            results= "" ; i=1 

            doc.xpath("//td").each do |item|
                results += item.content.to_s.delete("\n ").strip + (i%4!=0 ? "," : "\n") #class属性等無かったので項目数で改行箇所を判断
                i+=1
            end

            File.open("#{path}/#{filename}", "w:utf-8") { |f| f.write(results) }
        rescue OpenURI::HTTPError => e 
           puts "#{e.to_s}(#{url})" 
        end
        return nil
    end

    def getRankingLinkList(url)
        doc = parseHtmlFileAndEncodeUtf8(url)
        results = []

        doc.xpath("//td/a").each do |item|
            results << item["href"]
        end
        return results
    end
    
    def getWholeRanking(outputPath)
        getRankingLinkList(RankingListURL).each do |link|
            if link.count("0-9")>=5 then #年別の集計ランキングを除外
                getRankingAsCsv(RankingListURL +  link, outputPath, filename:link.delete("^0-9")+CSV)
            end
        end
    end

    def totalRankingCsv(csvPath, outputPath:Dir.pwd, filename:"total.csv")
        ranking = {}

        Dir.glob("#{csvPath}/*").each { |file|
            File.open(file, "r:utf-8") { |f| 
                f.each { |line|
                    array = line.split(",")
                    key = array[1]+"_ "+array[2] #作品名_作家名
                    value = array[3].to_i
                    if ranking.has_key?(key) then
                        ranking[key] = ranking[key].to_i + value
                    else
                        ranking[key] = value
                    end
                }
            }
        }
       
        results=""; i=1
        begin
            ranking.sort {|a,b| b[1]<=>a[1]}.each do |e|
                results += csv_nizer(i.to_s) + csv_nizer(e[0].split("_")[0]) + csv_nizer(e[0].split("_")[1].strip) + csv_nizer(e[1].to_s, true)
                i+=1
            end 
        rescue TypeError =>e
            p "#{e.to_s}(#{i})"
            p e.messsage
        end

        File.open("#{outputPath}/#{filename}", "w:utf-8") { |f| f.write(results) }
        return nil
    end
end