nicoscraper 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ #どんなライブラリか
2
+  ニコニコ動画から動画およびマイリスト情報を取得し、その情報に対して各種操作を行えます。タグやマイリスト検索結果からの抽出、および抽出結果に対する反復処理を行うメソッドも備え、ランキングサイト等の制作を支援します。
3
+
4
+ #簡単な概要
5
+  Movieクラス、Mylistクラス、Searcherモジュールから主要な機能が構成されています。基本的には、動画やマイリストのIDを指定してインスタンスを作り、そこから詳細な情報を取得するためのメソッドを実行したのち、さらに分析や加工を行う別のメソッドを実行する、という手順を踏みます。
6
+
7
+ ##動画情報の取得
8
+
9
+  例えば"sm1097445"という動画IDから、タイトルや動画の長さ、現在の閲覧数等の詳細な情報を知りたいときは、
10
+
11
+ ~~~~
12
+ require 'nicoscraper'
13
+
14
+ movie = Nicos::Movie::new("sm1097445")
15
+ movie.getInfo
16
+
17
+ p movie
18
+
19
+ <Nicos::Movie:0x00000002537aa8
20
+ @video_id="sm1097445",
21
+ @available=false, @title="【初音ミク】みくみくにしてあげる♪【してやんよ】",
22
+ @description="おまえら、みっくみくにしてやんよ。歌詞はhttp://ikamo.hp.infoseek.co.jp/mikumiku.txt(9/20 1:55修正)。上げている他のもの→mylist/1450136",
23
+ @thumbnail_url="http://tn-skr2.smilevideo.jp/smile?i=1097445",
24
+ @first_retrieve=1190218922,
25
+ @length=99,
26
+ @movie_type="flv",
27
+ @size_high=3906547,
28
+ @size_low=1688098,
29
+ @view_counter=9073614,
30
+ @comment_num=2553366,
31
+ @mylist_counter=183470,
32
+ @last_res_body="★███████████☆ ☆████████████●●●●███ ★█████... ",
33
+ @watch_url="http://www.nicovideo.jp/watch/sm1097445", @thumb_type="video",
34
+ @embeddable=1,
35
+ @no_live_play=0,
36
+ @tags_jp=["音楽", "初音ミク", "みくみくにしてあげる♪", "ミクオリジナル曲", "ika", "VOCALOID殿堂入り", "元気が出るミクうた", "VOCALOID", "初音ミク名曲リンク", "深夜みっく"],
37
+ @tags_tw=["彈幕強大", "把你MikuMiku掉♪", "週刊vocaloid排行榜第1名獲得曲", "翻譯歌曲", "台灣VOCALOID評論人氣曲", "最強彈幕傳說", "初音未來"],
38
+ @user_id=70391 >
39
+
40
+ ~~~~
41
+
42
+  このように、Movieクラスのインスタンス(以下「動画インスタンス」)のgetInfoメソッドを利用します。その結果、動画インスタンスにインスタンス変数として各種情報が付加されます。
43
+
44
+ ##マイリスト情報の取得
45
+  Mylistクラスもほぼ同様ですが、Mylistクラスのインスタンス(以下「マイリストインスタンス」)は、マイリスト情報の他に、そのマイリストが含む動画のインスタンスを自動的に生成します。つまり、
46
+
47
+ ~~~~
48
+ require 'nicoscraper'
49
+
50
+ mylist = Nicos::Mylist::new("")
51
+ mylist.getInfoLt
52
+
53
+ p mylist
54
+
55
+ # 結果
56
+ #<Nicos::Mylist:0x00000002884670
57
+  @mylist_id=15196568,
58
+ @movies=[
59
+ #<Nicos::Movie:0x0000000255a968
60
+ @video_id="sm8481759",
61
+ @available=true, @title="【Oblivion】おっさんの大冒険1(ゆっくり実況)",
62
+ ...
63
+ #<Nicos::Movie:0x0000000251a6b0
64
+ @video_id="sm8506034",
65
+ @available=true,
66
+ @title="【Oblivion】おっさんの大冒険2(ゆっくり実況)",
67
+ ...
68
+ ],
69
+ @available=true,
70
+ @title="【Oblivion】おっさんの大冒険",
71
+ ... >
72
+ ~~~~
73
+
74
+  というように、動画インスタンスを勝手につくりだして配列として保持します。
75
+
76
+ ##検索結果の取得
77
+
78
+  タグやマイリスト検索結果からの情報取得には、Searcherモジュールを使います。情報のソート方法の指定、取得する範囲の制限が可能です。
79
+
80
+ ~~~~
81
+ require 'nicoscraper'
82
+
83
+ t = Time.now
84
+ ytd = Date::new(t.year, t.month, t.day) - 1
85
+ yesterday = Time.local(ytd.year, ytd.month, ytd.day, 0, 0, 0).to_i
86
+
87
+ Searcher.byTag('ゆっくり実況プレイpart1リンク', 'post_new', nil) {
88
+ |result, page|
89
+
90
+ result.each { |e|
91
+ movie = Nicos::Movie.new(e['video_id'])
92
+ movie.getInfo
93
+
94
+ puts movie.title.toutf8 +
95
+ " is posted at " +
96
+ Time.at(movie.first_retrieve).to_s.toutf8
97
+
98
+ true if movie.first_retrieve <= yesterday
99
+ }
100
+ }
101
+ ~~~~
102
+
103
+  これは'ゆっくり実況プレイpart1リンク'というタグの付く動画を、post_new=投稿日時が新しい順からさかのぼって取得していき、取得した動画の日付が前日の0時0分を超えるまでそれを続けます。
104
+
105
+  ブロック内の第1引数には取得結果が与えられるのですが、これは動画1つ毎のコールバックではなく、32個分の配列です。なぜ32個のセットなのかと言うと、ご存知のようにニコニコ動画の検索画面はページで区切られており、Searcherモジュールの各メソッドはページ毎に情報を取得するためです。Htmlから取得するにしろAtomフィードから取得するにしろ、1ページに32個の動画情報が含まれています。そして、第2引数にはそのページ数が与えられます。
106
+
107
+  なお、ブロック内でtrueを返すことによりスクレイプは終わります。逆に言えば、trueを返さない限り検索結果全ての情報を取得しようとするため、十分に注意して下さい。上の例では、取得した動画の日付を調べ、3日前の0時0分より前の動画があればそこでループを終える設計です。
108
+
109
+
110
+ #より詳しい情報
111
+
112
+ は、こちらを御覧下さい。
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.2
@@ -5,356 +5,357 @@ require 'rubygems'
5
5
  require 'ruby-debug'
6
6
  require 'net/http'
7
7
 
8
+ module Nicos::Connector
9
+ class Connector
10
+ def initialize
11
+ # デフォルトのウェイト設定
12
+ @seqTime = 0
8
13
 
9
- class Connector
10
- def initialize
11
- # デフォルトのウェイト設定
12
- @seqTime = 0
13
-
14
- @waitConfig = {
15
- 'seqAccLimit' => 10, # 連続してリクエストする回数
16
- 'afterSeq' => 10, # 連続リクエスト後のウェイト
17
- 'each' => 1, # 連続リクエスト時の、1リクエスト毎のウェイト
18
-
19
- 'increment' => 1, # アクセス拒絶時の、次回以降の1リクエスト毎のウェイトの増加量
20
- '' => 100,
21
-
22
- 'deniedSeqReq'=> {
23
- 'retryLimit' => 3,
24
- 'wait' => 120
25
- },
26
-
27
- 'serverIsBusy'=> {
28
- 'retryLimit' => 3,
29
- 'wait' => 120
30
- },
31
-
32
- 'serviceUnavailable' => {
33
- 'retryLimit' => 3,
34
- 'wait' => 120
35
- },
36
-
37
- 'timedOut' => {
38
- 'retryLimit' => 3,
39
- 'wait' => 10
14
+ @waitConfig = {
15
+ 'seqAccLimit' => 10, # 連続してリクエストする回数
16
+ 'afterSeq' => 10, # 連続リクエスト後のウェイト
17
+ 'each' => 1, # 連続リクエスト時の、1リクエスト毎のウェイト
18
+
19
+ 'increment' => 1, # アクセス拒絶時の、次回以降の1リクエスト毎のウェイトの増加量
20
+ '' => 100,
21
+
22
+ 'deniedSeqReq'=> {
23
+ 'retryLimit' => 3,
24
+ 'wait' => 120
25
+ },
26
+
27
+ 'serverIsBusy'=> {
28
+ 'retryLimit' => 3,
29
+ 'wait' => 120
30
+ },
31
+
32
+ 'serviceUnavailable' => {
33
+ 'retryLimit' => 3,
34
+ 'wait' => 120
35
+ },
36
+
37
+ 'timedOut' => {
38
+ 'retryLimit' => 3,
39
+ 'wait' => 10
40
+ }
40
41
  }
41
- }
42
42
 
43
- @result = {}
44
- end
43
+ @result = {}
44
+ end
45
45
 
46
- private
47
-
48
- def notPublic
49
- # マイリスト非公開のときに403になる。後で専用の処理を入れるべき。
50
- puts "This movie/mylist is not public."
51
- @result = "notPublic"
52
- return { "order" => "terminate" }
53
- end
46
+ private
47
+
48
+ def notPublic
49
+ # マイリスト非公開のときに403になる。後で専用の処理を入れるべき。
50
+ puts "This movie/mylist is not public."
51
+ @result = "notPublic"
52
+ return { "order" => "terminate" }
53
+ end
54
54
 
55
- def limInCommunity
56
- puts "This movie/mylist is limited in comunity members."
57
- # ex. item_id -> 1294702905
58
- @result = "limInCommunity"
59
- return { "order" => "terminate" }
60
- end
55
+ def limInCommunity
56
+ puts "This movie/mylist is limited in comunity members."
57
+ # ex. item_id -> 1294702905
58
+ @result = "limInCommunity"
59
+ return { "order" => "terminate" }
60
+ end
61
61
 
62
- def notFound
63
- puts "This movie/mylist is not found."
64
- @result = "notFound"
65
- return { "order" => "terminate" }
66
- end
62
+ def notFound
63
+ puts "This movie/mylist is not found."
64
+ @result = "notFound"
65
+ return { "order" => "terminate" }
66
+ end
67
67
 
68
- def deleted
69
- puts "This movie/mylist is deleted."
70
- @result = "deleted"
71
- return { "order" => "terminate" }
72
- end
68
+ def deleted
69
+ puts "This movie/mylist is deleted."
70
+ @result = "deleted"
71
+ return { "order" => "terminate" }
72
+ end
73
73
 
74
- def deniedSeqReq
75
- puts "Denied sequential requests."
76
- sleep @waitConfig["deniedSeqReq"]
77
- @result = "deniedSeqReq"
78
- return { "order" => "retry" }
79
- end
74
+ def deniedSeqReq
75
+ puts "Denied sequential requests."
76
+ sleep @waitConfig["deniedSeqReq"]
77
+ @result = "deniedSeqReq"
78
+ return { "order" => "retry" }
79
+ end
80
80
 
81
- def serverIsBusy
82
- puts "The server is busy."
83
- sleep @waitConfig["serverIsBusy"]
84
- @result = "serverIsBusy"
85
- return { "order" => "retry" }
86
- end
81
+ def serverIsBusy
82
+ puts "The server is busy."
83
+ sleep @waitConfig["serverIsBusy"]
84
+ @result = "serverIsBusy"
85
+ return { "order" => "retry" }
86
+ end
87
87
 
88
- def serviceUnavailable
89
- puts "Service unavailable."
90
- sleep @waitConfig["serviceUnavailable"]
91
- @result = "serviceUnavailable"
92
- return { "order" => "retry" }
93
- end
88
+ def serviceUnavailable
89
+ puts "Service unavailable."
90
+ sleep @waitConfig["serviceUnavailable"]
91
+ @result = "serviceUnavailable"
92
+ return { "order" => "retry" }
93
+ end
94
94
 
95
- def timedOut
96
- puts "Request timed out."
97
- sleep @waitConfig["timedOut"]
98
- @result = "timedOut"
99
- return { "order" => "retry" }
100
- end
95
+ def timedOut
96
+ puts "Request timed out."
97
+ sleep @waitConfig["timedOut"]
98
+ @result = "timedOut"
99
+ return { "order" => "retry" }
100
+ end
101
101
 
102
- def success(resBody)
103
- sleep @waitConfig["each"]
104
- @seqTime += 1
105
-
106
- if @seqTime >= @waitConfig["seqAccLimit"]
107
- sleep @waitConfig["afterSeq"]
108
- @seqTime = 0
102
+ def success(resBody)
103
+ sleep @waitConfig["each"]
104
+ @seqTime += 1
105
+
106
+ if @seqTime >= @waitConfig["seqAccLimit"]
107
+ sleep @waitConfig["afterSeq"]
108
+ @seqTime = 0
109
+ end
110
+ return { "order" => "success", "body" => resBody }
109
111
  end
110
- return { "order" => "success", "body" => resBody }
111
- end
112
112
 
113
- def wait(status)
114
- puts "Wait for " + waitTime + " second."
115
- sleep @waitConfig[status.to_s]
116
- end
117
-
118
- public
113
+ def wait(status)
114
+ puts "Wait for " + waitTime + " second."
115
+ sleep @waitConfig[status.to_s]
116
+ end
117
+
118
+ public
119
119
 
120
- def setWait(waitConfig)
121
- if waitConfig != nil
122
- @waitConfig = mixin(@waitConfig, waitConfig)
120
+ def setWait(waitConfig)
121
+ if waitConfig != nil
122
+ @waitConfig = mixin(@waitConfig, waitConfig)
123
+ end
123
124
  end
124
125
  end
125
- end
126
126
 
127
127
 
128
- class XmlConnector < Connector
129
- def get (host, entity)
130
- response = nil
131
-
132
- begin
133
- puts "Request to " + host + entity
134
- Net::HTTP.start(host, 80) { |http|
135
- response = http.get(entity)
136
- }
128
+ class Xml < Connector
129
+ def get (host, entity)
130
+ response = nil
131
+
132
+ begin
133
+ puts "Request to " + host + entity
134
+ Net::HTTP.start(host, 80) { |http|
135
+ response = http.get(entity)
136
+ }
137
+
138
+ rescue => e
139
+ puts e
140
+ rescue Timeout::Error => e
141
+ timeOut
137
142
 
138
- rescue => e
139
- puts e
140
- rescue Timeout::Error => e
141
- timeOut
142
-
143
- else
144
- res = case response
145
- when Net::HTTPSuccess
146
- reviewRes( response.body.force_encoding("UTF-8") )
147
- # return response.body.force_encoding("UTF-8")
148
- # when Net::HTTPRedirection
149
- # fetch(response['location'], limit - 1)
150
- when Net::HTTPForbidden
151
- forbidden
152
- when Net::HTTPNotFound
153
- notFound
154
- when Net::HTTPServiceUnavailable
155
- serviceUnavailable
156
143
  else
157
- unknownError
158
- end
159
- end until res["order"] == "success" ||
160
- res["order"] == "terminate"
144
+ res = case response
145
+ when Net::HTTPSuccess
146
+ reviewRes( response.body.force_encoding("UTF-8") )
147
+ # return response.body.force_encoding("UTF-8")
148
+ # when Net::HTTPRedirection
149
+ # fetch(response['location'], limit - 1)
150
+ when Net::HTTPForbidden
151
+ forbidden
152
+ when Net::HTTPNotFound
153
+ notFound
154
+ when Net::HTTPServiceUnavailable
155
+ serviceUnavailable
156
+ else
157
+ unknownError
158
+ end
159
+ end until res["order"] == "success" ||
160
+ res["order"] == "terminate"
161
161
 
162
- res
162
+ res
163
+ end
163
164
  end
164
- end
165
165
 
166
- class MylistAtomConnector < XmlConnector
167
- private
166
+ class MylistAtom < Xml
167
+ private
168
168
 
169
- def forbidden
170
- # マイリストが非公開の場合、html/Atomのどちらへのリクエストであっても、403が返ってくる。
171
- notPublic
172
- end
169
+ def forbidden
170
+ # マイリストが非公開の場合、html/Atomのどちらへのリクエストであっても、403が返ってくる。
171
+ notPublic
172
+ end
173
173
 
174
- def reviewRes(resBody)
175
- if # アクセス集中時
176
- /大変ご迷惑をおかけいたしますが、しばらく時間をあけてから再度検索いただくようご協力をお願いいたします。/ =~
177
- resBody.force_encoding("UTF-8")
178
- then
179
- serverIsBusy
180
- else
181
- success(resBody)
182
- end
174
+ def reviewRes(resBody)
175
+ if # アクセス集中時
176
+ /大変ご迷惑をおかけいたしますが、しばらく時間をあけてから再度検索いただくようご協力をお願いいたします。/ =~
177
+ resBody.force_encoding("UTF-8")
178
+ then
179
+ serverIsBusy
180
+ else
181
+ success(resBody)
182
+ end
183
+ end
183
184
  end
184
- end
185
185
 
186
- class SearchByTagAtomConnector < XmlConnector
187
- private
186
+ class TagAtom < Xml
187
+ private
188
188
 
189
- def forbidden
190
- # マイリストが非公開の場合、html/Atomのどちらへのリクエストであっても、403が返ってくる。
191
- notPublic
192
- end
189
+ def forbidden
190
+ # マイリストが非公開の場合、html/Atomのどちらへのリクエストであっても、403が返ってくる。
191
+ notPublic
192
+ end
193
193
 
194
- def reviewRes(resBody)
195
- if # アクセス集中時
196
- /大変ご迷惑をおかけいたしますが、しばらく時間をあけてから再度検索いただくようご協力をお願いいたします。/ =~
197
- resBody.force_encoding("UTF-8")
198
- then
199
- serverIsBusy
200
- else
201
- success(resBody)
202
- end
194
+ def reviewRes(resBody)
195
+ if # アクセス集中時
196
+ /大変ご迷惑をおかけいたしますが、しばらく時間をあけてから再度検索いただくようご協力をお願いいたします。/ =~
197
+ resBody.force_encoding("UTF-8")
198
+ then
199
+ serverIsBusy
200
+ else
201
+ success(resBody)
202
+ end
203
+ end
203
204
  end
204
- end
205
205
 
206
- class GetThumbInfoConnector < XmlConnector
207
- private
206
+ class GetThumbInfo < Xml
207
+ private
208
208
 
209
- def reviewRes(resBody)
210
- r = resBody.force_encoding("UTF-8")
209
+ def reviewRes(resBody)
210
+ r = resBody.force_encoding("UTF-8")
211
211
 
212
- if # getThumbInfoは、該当する動画がない・削除済み・コミュニティ限定でも200が返ってくる。
213
- /<nicovideo_thumb_response\sstatus=\"fail\">/ =~ r
214
- if /<code>NOT_FOUND<\/code>/ =~ r
215
- notFound
216
- elsif /<code>DELETED<\/code>/ =~ r
217
- deleted
218
- elsif /<code>COMMUNITY<\/code>/ =~ r
219
- limInCommunity
212
+ if # getThumbInfoは、該当する動画がない・削除済み・コミュニティ限定でも200が返ってくる。
213
+ /<nicovideo_thumb_response\sstatus=\"fail\">/ =~ r
214
+ if /<code>NOT_FOUND<\/code>/ =~ r
215
+ notFound
216
+ elsif /<code>DELETED<\/code>/ =~ r
217
+ deleted
218
+ elsif /<code>COMMUNITY<\/code>/ =~ r
219
+ limInCommunity
220
+ else
221
+ serverIsBusy
222
+ end
220
223
  else
221
- serverIsBusy
222
- end
223
- else
224
- success(resBody)
225
- end
224
+ success(resBody)
225
+ end
226
+ end
226
227
  end
227
- end
228
228
 
229
- class HtmlConnector < Connector
230
- def initialize(mode)
231
- @mode = mode
232
- # デフォルトのウェイト設定
233
- @waitConfig = {
234
- 'consec_count' => 10, # 連続してリクエストする回数
235
- 'consec_wait' => 10, # 連続リクエスト後のウェイト
236
- 'each' => 10, # 連続リクエスト時の、1リクエスト毎のウェイト
237
-
238
- '200-abnormal' => 300, # アクセス拒絶時(「短時間での連続アクセスは・・・」)の場合の再試行までの時間
239
- 'unavailable' => 10,
240
- '403' => 300, # "403"時の再試行までのウェイト
241
- '404' => 300, # "403"時の再試行までのウェイト
242
- 'increment' => 1, # アクセス拒絶時の、次回以降の1リクエスト毎のウェイトの増加量
243
-
244
- 'timeout' => 10, # タイムアウト時の、再試行までのウェイト
245
- '500' => 10, # "500"時の再試行までのウェイト
246
- '503' => 10, # "503"時の再試行までのウェイト
247
-
248
- 'retryLimit' => 3 # 再試行回数の限度
249
- }
250
-
251
- # 1つの検索結果画面に表示される動画の数。現時点では32個がデフォルトの模様。
252
- @NumOfSearched = 32
253
-
254
- @mech = Mechanize.new
255
- # メモリ節約のため、Mechanizeの履歴機能を切る。
256
- @mech.max_history = 1
229
+ class HtmlConnector < Connector
230
+ def initialize(mode)
231
+ @mode = mode
232
+ # デフォルトのウェイト設定
233
+ @waitConfig = {
234
+ 'consec_count' => 10, # 連続してリクエストする回数
235
+ 'consec_wait' => 10, # 連続リクエスト後のウェイト
236
+ 'each' => 10, # 連続リクエスト時の、1リクエスト毎のウェイト
237
+
238
+ '200-abnormal' => 300, # アクセス拒絶時(「短時間での連続アクセスは・・・」)の場合の再試行までの時間
239
+ 'unavailable' => 10,
240
+ '403' => 300, # "403"時の再試行までのウェイト
241
+ '404' => 300, # "403"時の再試行までのウェイト
242
+ 'increment' => 1, # アクセス拒絶時の、次回以降の1リクエスト毎のウェイトの増加量
243
+
244
+ 'timeout' => 10, # タイムアウト時の、再試行までのウェイト
245
+ '500' => 10, # "500"時の再試行までのウェイト
246
+ '503' => 10, # "503"時の再試行までのウェイト
247
+
248
+ 'retryLimit' => 3 # 再試行回数の限度
249
+ }
250
+
251
+ # 1つの検索結果画面に表示される動画の数。現時点では32個がデフォルトの模様。
252
+ @NumOfSearched = 32
257
253
 
258
- @consec_count = 0
259
- end
260
-
261
- public
262
-
263
- def errorStatus(ex)
264
- # 再試行回数が
265
- @retryTime += 1
266
- if @retryTime >= @wait['allowance_time']
267
- return false
268
- end
254
+ @mech = Mechanize.new
255
+ # メモリ節約のため、Mechanizeの履歴機能を切る。
256
+ @mech.max_history = 1
269
257
 
270
- case ex.response_code
271
- when '403' then
272
- sleep @wait['403']
273
- warn "403"
274
- when '500' then
275
- sleep @wait['500']
276
- warn "500"
277
- when '503' then
278
- sleep @wait['503']
279
- warn "503"
280
- else
281
- warn "Server error: #{ex.code}"
282
- return false
258
+ @consec_count = 0
283
259
  end
284
-
285
- @connection = false
286
- @failed += 1
287
- end
260
+
261
+ public
288
262
 
289
- def htmlReq (url, request, procedure)
290
- @failed = 0
263
+ def errorStatus(ex)
264
+ # 再試行回数が
265
+ @retryTime += 1
266
+ if @retryTime >= @wait['allowance_time']
267
+ return false
268
+ end
291
269
 
292
- # 再試行ループ
293
- begin
294
- eachWait
295
- @connection = nil
296
- request.call(url)
297
-
298
- # タイムアウト時処理
299
- rescue TimeoutError
300
- timeOut
301
- retry
302
-
303
- # Mechanizeでアクセスし、200以外のステータスが返ってきた時
304
- # 実際に該当するコードが返ってきたことがないので、正常に動くか不明
305
- rescue Mechanize::ResponseCodeError => ex
306
- if errorStatus(ex) then retry
307
- else break end
308
-
309
- # HTTP Status:200時の処理
310
- else
311
- procedure.call
312
-
313
- # 失敗カウントが指定回数を超えたらループを終わる。
314
- if @failed >= @wait['allowance_time'] then
315
- puts 'Exceeded the limit of retry time.'
316
- @connection = false
317
- break
270
+ case ex.response_code
271
+ when '403' then
272
+ sleep @wait['403']
273
+ warn "403"
274
+ when '500' then
275
+ sleep @wait['500']
276
+ warn "500"
277
+ when '503' then
278
+ sleep @wait['503']
279
+ warn "503"
280
+ else
281
+ warn "Server error: #{ex.code}"
282
+ return false
318
283
  end
319
- end until @connection
284
+
285
+ @connection = false
286
+ @failed += 1
287
+ end
320
288
 
321
- # 連続アクセスカウント+1
322
- @consec_count += 1
323
- # 成功 = true / 失敗 = false
324
- return @connection
325
- end
289
+ def htmlReq (url, request, procedure)
290
+ @failed = 0
326
291
 
327
- def htmlGet (host, entity)
328
- htmlReq(
329
- host + entity,
330
- lambda { |url|
331
- t = Thread.new do
332
- @mech.get(url)
333
- puts "Requesting for " + url
334
- end
335
- t.join
336
- },
337
- # HTTP Status:200時の処理
338
- lambda {
339
- # 連続アクセス拒絶メッセージが返ってきた時
340
- if /短時間での連続アクセスはご遠慮ください/ =~ @mech.page.search('/html').text then
341
- puts 'Access rejected.'
292
+ # 再試行ループ
293
+ begin
294
+ eachWait
295
+ @connection = nil
296
+ request.call(url)
297
+
298
+ # タイムアウト時処理
299
+ rescue TimeoutError
300
+ timeOut
301
+ retry
302
+
303
+ # Mechanizeでアクセスし、200以外のステータスが返ってきた時
304
+ # 実際に該当するコードが返ってきたことがないので、正常に動くか不明
305
+ rescue Mechanize::ResponseCodeError => ex
306
+ if errorStatus(ex) then retry
307
+ else break end
308
+
309
+ # HTTP Status:200時の処理
310
+ else
311
+ procedure.call
312
+
313
+ # 失敗カウントが指定回数を超えたらループを終わる。
314
+ if @failed >= @wait['allowance_time'] then
315
+ puts 'Exceeded the limit of retry time.'
342
316
  @connection = false
343
- @failed += 1
344
-
345
- # ウェイトを置いた後、今後のページ毎のウェイトを増やす。
346
- puts 'Waiting for ' + @wait['rejected'] + 's.'
347
- sleep @wait['rejected']
348
- @wait['each'] += @wait['increment']
349
- puts 'Increased each @wait by ' + @wait['increment'] + 'sec.'
350
- else
351
- @connection = true
317
+ break
352
318
  end
353
- }
354
- )
355
-
356
- return @mech.page
357
- end
319
+ end until @connection
320
+
321
+ # 連続アクセスカウント+1
322
+ @consec_count += 1
323
+ # 成功 = true / 失敗 = false
324
+ return @connection
325
+ end
358
326
 
359
- attr_reader :mech
327
+ def htmlGet (host, entity)
328
+ htmlReq(
329
+ host + entity,
330
+ lambda { |url|
331
+ t = Thread.new do
332
+ @mech.get(url)
333
+ puts "Requesting for " + url
334
+ end
335
+ t.join
336
+ },
337
+ # HTTP Status:200時の処理
338
+ lambda {
339
+ # 連続アクセス拒絶メッセージが返ってきた時
340
+ if /短時間での連続アクセスはご遠慮ください/ =~ @mech.page.search('/html').text then
341
+ puts 'Access rejected.'
342
+ @connection = false
343
+ @failed += 1
344
+
345
+ # ウェイトを置いた後、今後のページ毎のウェイトを増やす。
346
+ puts 'Waiting for ' + @wait['rejected'] + 's.'
347
+ sleep @wait['rejected']
348
+ @wait['each'] += @wait['increment']
349
+ puts 'Increased each @wait by ' + @wait['increment'] + 'sec.'
350
+ else
351
+ @connection = true
352
+ end
353
+ }
354
+ )
355
+
356
+ return @mech.page
357
+ end
358
+
359
+ attr_reader :mech
360
+ end
360
361
  end