AtCoderでユーザーおよび問題をイロレーティングで評価してみた その2

# 概要
　[前回の投稿](http://qiita.com/YSRKEN/items/152e01788b7c6fd55d6f)で、ABC(AtCoder Beginner Contest)の問題のレーティングを計算しました。**しかしどうせならARCの結果も欲しい！**
　ということでそちらも取得して計算してみました。また、プログラムも多少改良しました。

# 複数スレッドによる並列クロール
　前回のコードでは、1ページづつOpen-URIでアクセスしてからNokogiriで読み取っていました。しかし**それだと遅い！明らかに遅い！**
　ということで、様々な手段を講じてみました。

- 「第三者のAPIを使って楽できないか」……[非公式APIその1](https://gist.github.com/kenkoooo/4618461adf0f5b4d3e3e)や[非公式APIその2](http://qiita.com/skht777/items/7ac7c7115d00152f4e39)にアクセスすれば楽できるのではと思いましたが、非公式APIも結局は**クロールして結果の一部をJSONで返すだけ**ですので、今回のような高頻度アクセスに向いていません(具体的には遅い＆許容レスポンス量が低い)。よってボツ
- 「em-http-requestで非同期リクエスト」……[このページ](http://qiita.com/shinaisan/items/0a9f4aa01dfc3bae8f1b)などを見ながら書いたら確かに速いんですよね。ただ、使用しているうちになぜか**AtCoderへのリクエストが全て失敗する**という謎のトラブルに見舞われまして、結局ボツ
- 「threadで複数スレッドを生成してopenしまくる」……冷静に考えると雑な発想ですが、結局これが一番当たりました。ただし、各ページごとにスレッドを生成するとオーバーヘッドがデカすぎるので、とりあえずコンテスト毎にスレッドを生成して並行動作させる作戦にしました。意味不明なことに「Rubyのthreadはマルチスレッド動作しない」**(同時に実行されるネイティブスレッドが常に1つ)**のですが、I/O待ちについては謎制限(GVL)を解いてくれるため、今回のような並列アクセス用途にはぴったりですね。

　結果、最後の手段を採用して、高速化を果たしました。コードはこちら。

```download2.rb
# 対戦結果を生成する

require 'nokogiri'
require 'open-uri'
require 'thread'

# まず、全コンテストについての情報を入手する
contest_list = []
1.upto(33){|i|contest_list.push("abc#{sprintf('%03d', i)}")}
1.upto(47){|i|contest_list.push("arc#{sprintf('%03d', i)}")}
# 次に、各コンテスト毎に結果を取得し取り込む
output = {}
query_list = ['A', 'B', 'C', 'D']
threads = []
start_time = Time.now
contest_list.each{|contest_name|
	# スレッド生成
	threads << Thread.new{
		# 下準備
		name_list = {}
		ac_name_list = {}
		query_list.each{|query|ac_name_list[query] = Hash.new(0)}
		# とり込み実行
		1.upto(0/0.0){|page|
			# ページをダウンロードする
			url = "http://#{contest_name}.contest.atcoder.jp/submissions/all/#{page}"
			charset = ''
			while true
				begin
					html = open(url){|f|charset=f.charset;f.read}
					break
				rescue
					next
				end
			end
			# 結果を解析する
			doc = Nokogiri::HTML.parse(html, nil, charset)
			nodes = doc.css('table[class="table table-bordered table-striped table-wb"] > tbody > tr')
			if nodes.size == 0 then
				break
			end
			puts "#{contest_name} #{page} #{nodes.size}"
			nodes.each{|node|
				query = (node.css('td > a')[0].inner_text)[0]
				name = node.css('td > a')[1].inner_text
				result = node.css('td > span').inner_text
				name_list[name] = 1
				if result == 'AC' then
					begin
						ac_name_list[query][name] = 1
					rescue
					end
				end
			}
		}
		# 結果を書き出す
		name_list.keys.each{|name|
			query_list.each{|query|
				output["[#{contest_name}_#{query}],#{name},#{(ac_name_list[query][name] == 1 ? 'Win' : 'Lose')}"] = 1
			}
		}
	}
}
threads.each{|t|t.join}
end_time = Time.now
File.write('log.txt', output.keys.join("\n"))
puts start_time
puts end_time
puts end_time - start_time
```

# 計算結果(AtCoder Beginner Contest)
　こちらは[前回](http://qiita.com/YSRKEN/items/152e01788b7c6fd55d6f#%E7%B5%90%E6%9E%9C)と同じ結果になりました。レーティングの数値自体は微妙に変わっていますが、ARCの結果も含めたので仕方ないでしょう。

[問題Aで最易](http://abc001.contest.atcoder.jp/tasks/abc001_1)　[問題Aで最難](http://abc022.contest.atcoder.jp/tasks/abc022_a)
[問題Bで最易](http://abc016.contest.atcoder.jp/tasks/abc016_2)　[問題Bで最難](http://abc027.contest.atcoder.jp/tasks/abc027_b)
[問題Cで最易](http://abc012.contest.atcoder.jp/tasks/abc012_3)　[問題Cで最難](http://abc008.contest.atcoder.jp/tasks/abc008_3)
[問題Dで最易](http://abc028.contest.atcoder.jp/tasks/abc028_d)　[問題Dで最難](http://abc025.contest.atcoder.jp/tasks/abc025_d)

|No.|Q.|rate|No.|Q.|rate|No.|Q.|rate|No.|Q.|rate|
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|**1**|**A**|**733**|10|A|878|19|A|1002|28|A|915|
|1|B|1283|10|B|1157|19|B|1461|28|B|1227|
|1|C|1949|10|C|1388|19|C|1643|28|C|1344|
|1|D|2038|10|D|2119|19|D|1761|**28**|**D**|**1537**|
|2|A|1012|11|A|997|20|A|965|29|A|826|
|2|B|1158|11|B|1293|20|B|1018|29|B|1086|
|2|C|1388|11|C|1652|20|C|2028|29|C|1463|
|2|D|1880|11|D|2148|20|D|2366|29|D|1969|
|3|A|988|12|A|1016|21|A|989|30|A|990|
|3|B|1381|12|B|1192|21|B|1304|30|B|1571|
|3|C|1499|**12**|**C**|**1243**|21|C|1970|30|C|1758|
|3|D|2244|12|D|1733|21|D|2017|30|D|1929|
|4|A|949|13|A|1033|**22**|**A**|**1157**|31|A|835|
|4|B|1247|13|B|1226|22|B|1319|31|B|1015|
|4|C|1430|13|C|1988|22|C|1947|31|C|1945|
|4|D|2163|13|D|2040|22|D|2075|31|D|2132|
|5|A|981|14|A|1116|23|A|887|32|A|985|
|5|B|1136|14|B|1547|23|B|1499|32|B|1506|
|5|C|1608|14|C|1777|23|C|2080|32|C|1802|
|5|D|1905|14|D|2086|23|D|2064|32|D|1956|
|6|A|993|15|A|906|24|A|1045|33|A|888|
|6|B|1583|15|B|1078|24|B|1251|33|B|1133|
|6|C|1773|15|C|1670|24|C|1685|33|C|1424|
|6|D|1957|15|D|1795|24|D|2222|33|D|2382|
|7|A|946|16|A|934|25|A|1032|||||
|7|B|1188|**16**|**B**|**978**|25|B|1187|||||
|7|C|1782|16|C|1736|25|C|2179|||||
|7|D|2086|16|D|1855|**25**|**D**|**2486**|||||
|8|A|887|17|A|1006|26|A|852|||||
|8|B|1519|17|B|1293|26|B|1229|||||
|**8**|**C**|**2211**|17|C|2013|26|C|1768|||||
|8|D|2310|17|D|2156|26|D|1842|||||
|9|A|976|18|A|1071|27|A|959|||||
|9|B|1190|18|B|1277|**27**|**B**|**1633**|||||
|9|C|2184|18|C|2070|27|C|2032|||||
|9|D|2287|18|D|2071|27|D|2154|||||

# 計算結果(AtCoder Regular Contest)
　こちらの結果も載せてみました。ABCの平均レーティングが1516であるのに対し、ARCの平均レーティングが1873となっているのでどれぐらい難しくなっているかの参考になるかと思われます。

[問題Aで最易](http://arc014.contest.atcoder.jp/tasks/arc014_1)　[問題Aで最難](http://arc042.contest.atcoder.jp/tasks/arc042_a)
[問題Bで最易](http://arc012.contest.atcoder.jp/tasks/arc012_2)　[問題Bで最難](http://arc047.contest.atcoder.jp/tasks/arc047_b)
[問題Cで最易](http://arc006.contest.atcoder.jp/tasks/arc006_3)　[問題Cで最難](http://arc019.contest.atcoder.jp/tasks/arc019_3)
[問題Dで最易](http://arc006.contest.atcoder.jp/tasks/arc006_4)　[問題Dで最難](http://arc022.contest.atcoder.jp/tasks/arc022_4)

|No.|Q.|rate|No.|Q.|rate|No.|Q.|rate|No.|Q.|rate|
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|1|A|1236|13|A|1336|25|A|1009|37|A|1039|
|1|B|1630|13|B|1552|25|B|2015|37|B|1878|
|1|C|2133|13|C|2161|25|C|2200|37|C|2118|
|1|D|2555|13|D|2417|25|D|2591|37|D|2352|
|2|A|968|**14**|**A**|**873**|26|A|1018|38|A|1039|
|2|B|1585|14|B|1482|26|B|1764|38|B|2023|
|2|C|1695|14|C|1937|26|C|2202|38|C|2451|
|2|D|2600|14|D|2174|26|D|2265|38|D|2508|
|3|A|1117|15|A|992|27|A|1029|39|A|1302|
|3|B|1409|15|B|1317|27|B|2029|39|B|1924|
|3|C|2258|15|C|2302|27|C|2110|39|C|2181|
|3|D|2317|15|D|2566|27|D|2424|39|D|2440|
|4|A|1193|16|A|1034|28|A|1111|40|A|1072|
|4|B|1899|16|B|1299|28|B|2002|40|B|1895|
|4|C|2236|16|C|2313|28|C|2075|40|C|1900|
|4|D|2318|16|D|2473|28|D|2483|40|D|2649|
|5|A|1147|17|A|1114|29|A|1317|41|A|1088|
|5|B|1708|17|B|1645|29|B|2287|41|B|1768|
|5|C|1896|17|C|1968|29|C|2268|41|C|2103|
|5|D|2565|17|D|2287|29|D|2369|41|D|2472|
|6|A|1125|18|A|1030|30|A|1264|**42**|**A**|**1480**|
|6|B|1616|18|B|1867|30|B|1945|42|B|1794|
|**6**|**C**|**1656**|18|C|2206|30|C|2325|42|C|2087|
|**6**|**D**|**2172**|18|D|2563|30|D|2643|42|D|2279|
|7|A|1051|19|A|1144|31|A|971|43|A|1415|
|7|B|1290|19|B|1710|31|B|1938|43|B|2047|
|7|C|1877|**19**|**C**|**2481**|31|C|2182|43|C|2390|
|7|D|2451|19|D|2241|31|D|2561|43|D|2664|
|8|A|1045|20|A|973|32|A|1007|44|A|1295|
|8|B|1477|20|B|1630|32|B|1812|44|B|2129|
|8|C|2179|20|C|2314|32|C|2290|44|C|2303|
|8|D|2277|20|D|2590|32|D|2508|44|D|2321|
|9|A|963|21|A|1128|33|A|1060|45|A|1050|
|9|B|1659|21|B|1941|33|B|1596|45|B|1963|
|9|C|2257|21|C|2269|33|C|1992|45|C|2213|
|9|D|2554|21|D|2609|33|D|2437|45|D|2702|
|10|A|1064|22|A|1130|34|A|1125|46|A|1052|
|10|B|1792|22|B|1916|34|B|1638|46|B|1930|
|10|C|2328|22|C|2010|34|C|1942|46|C|2262|
|10|D|2453|**22**|**D**|**2985**|34|D|2431|46|D|2516|
|11|A|1147|23|A|1135|35|A|1114|47|A|925|
|11|B|1562|23|B|1695|35|B|1711|**47**|**B**|**2373**|
|11|C|1819|23|C|2143|35|C|2071|47|C|2360|
|11|D|2590|23|D|2383|35|D|2236|47|D|2671|
|12|A|997|24|A|1168|36|A|1211|||||
|**12**|**B**|**1172**|24|B|1893|36|B|1776|||||
|12|C|2151|24|C|2120|36|C|2302|||||
|12|D|2639|24|D|2384|36|D|2288|||||

#計算結果(ユーザーランキング)
　ABC・ARC全体で7719名でした。ちなみに私は**rate1442の4428位なので雑魚**です。

|rank|ユーザ名|rate|
|--:|:--|--:|
|1|kmjp|3415|
|2|chabo|2934|
|3|おねーちゃん|2881|
|4|coolwanglu|2880|
|5|/\_/\ < queue!!|2821|
|6|persim|2739|
|7|kcm1700(韓)|2732|
|8|nekoyuki|2711|
|9|tkori|2700|
|10|ainu7|2672|
|11|Doju|2650|
|12|watashi|2649|
|13|エスカルきゅうり|2648|
|14|mamekin|2646|
|15|rook|2642|
|16|Otama|2629|
|17|duo|2625|
|18|Oleg|2616|
|19|ユーザ名|2616|
|20|rhd_may_20150300|2602|

　分布のヒストグラムも載せておきます。[前回](http://qiita.com/YSRKEN/items/152e01788b7c6fd55d6f#%E7%B5%90%E6%9E%9C)に比べてより中央(1500)寄りになったのではないでしょうか。
![hist2.png](https://qiita-image-store.s3.amazonaws.com/0/29802/8bbfdfdd-ab42-9328-7c68-310f0cd8ff0f.png)