nicoscraper 0.2.9 → 0.2.10
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.
- data/README.md +47 -41
- data/VERSION +1 -1
- data/lib/classes/connector.rb +177 -47
- data/lib/classes/movie.rb +84 -123
- data/lib/classes/mylist.rb +128 -136
- data/lib/classes/parser.rb +165 -91
- data/lib/classes/searcher.rb +138 -80
- data/lib/config/wait.rb +17 -17
- data/nicoscraper.gemspec +2 -2
- data/test/movie_spec.rb +18 -40
- data/test/mylist_spec.rb +98 -14
- data/test/searcher_spec.rb +96 -75
- metadata +3 -3
data/README.md
CHANGED
@@ -9,8 +9,8 @@ NicoScraper
|
|
9
9
|
**Author:** Masami Yonehara
|
10
10
|
**Copyright:** 2011
|
11
11
|
**License:** MIT License
|
12
|
-
**Latest Version:** 0.2.
|
13
|
-
**Release Date:** Sep
|
12
|
+
**Latest Version:** 0.2.10
|
13
|
+
**Release Date:** Sep 30th 2011
|
14
14
|
|
15
15
|
|
16
16
|
何をするライブラリ?
|
@@ -130,7 +130,7 @@ NicoScraper
|
|
130
130
|
searchByTag = Nicos::Searcher::ByTag.new()
|
131
131
|
searchByTag.execute(
|
132
132
|
'VOCALOID',
|
133
|
-
|
133
|
+
:post_new
|
134
134
|
) { |result, status|
|
135
135
|
terminate = false
|
136
136
|
|
@@ -194,34 +194,31 @@ NicoScraper
|
|
194
194
|
それを防ぐための措置の一つが、`"continue"`を明示的に返さないとスクレイピングが継続しない仕様ですが、もう一つ、アクセス中のウェイトを任意に設定できるようにしています。具体的には、連続リクエストの上限回数、連続リクエスト後のウェイト、1リクエスト毎のウェイト、連続アクセス拒絶時やサーバ混雑時の再試行までのウェイトなどです。ウェイトの設定は、以下のように`Nicos::Connector::Config::waitConfig`に与えられています。以下はデフォルトの設定です。
|
195
195
|
|
196
196
|
Nicos::Connector::Config::waitConfig = {
|
197
|
+
:seqAccLimit => 10, # 連続してリクエストする回数
|
198
|
+
:afterSeq => 10, # 連続リクエスト後のウェイト(以下全て単位は秒)
|
199
|
+
:each => 1, # 連続リクエスト時の、1リクエスト毎のウェイト
|
197
200
|
|
198
|
-
#
|
199
|
-
'seqAccLimit' => 10, # 連続してリクエストする回数
|
200
|
-
'afterSeq' => 10, # 連続リクエスト後のウェイト(以下全て単位は秒)
|
201
|
-
'each' => 5, # 連続リクエスト時の、1リクエスト毎のウェイト
|
201
|
+
:increment => 1, # 例外発生時の、次回以降の1リクエスト毎のウェイトの増加量
|
202
202
|
|
203
|
-
#
|
204
|
-
|
205
|
-
|
206
|
-
'wait' => 120 # 再試行までのウェイト
|
203
|
+
:deniedSeqReq=> { # 連続アクセス拒絶時
|
204
|
+
:retryLimit => 3, # 再試行回数の上限
|
205
|
+
:wait => 120 # 再試行までのウェイト
|
207
206
|
},
|
208
207
|
|
209
|
-
|
210
|
-
|
211
|
-
|
208
|
+
:serverIsBusy=> { # サーバ混雑時
|
209
|
+
:retryLimit => 3,
|
210
|
+
:wait => 120
|
212
211
|
},
|
213
212
|
|
214
|
-
|
215
|
-
|
216
|
-
|
213
|
+
:serviceUnavailable => { # 503時
|
214
|
+
:retryLimit => 3,
|
215
|
+
:wait => 120
|
217
216
|
},
|
218
217
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
}
|
223
|
-
|
224
|
-
'increment' => 1 # 異常ステータス時の、次回以降の1リクエスト毎のウェイトの増加量
|
218
|
+
:timedOut => { # タイムアウト時
|
219
|
+
:retryLimit => 3,
|
220
|
+
:wait => 10
|
221
|
+
}
|
225
222
|
}
|
226
223
|
|
227
224
|
###連続リクエストとは?
|
@@ -271,10 +268,10 @@ NicoScraper
|
|
271
268
|
なお、setWaitメソッドは指定したキーの部分のみを上書きするため、設定毎に上記の書式のハッシュオブジェクトを用意する必要はありません。
|
272
269
|
|
273
270
|
wait = {
|
274
|
-
|
271
|
+
:seqAccLimit => 100,
|
275
272
|
|
276
|
-
|
277
|
-
|
273
|
+
:deniedSeqReq => {
|
274
|
+
:wait => 1200
|
278
275
|
}
|
279
276
|
}
|
280
277
|
|
@@ -323,9 +320,32 @@ Mylistクラスのインスタンス
|
|
323
320
|
GitHubを経由して下さってもいいのですが、まだ慣れていないので対応が遅れるかもしれません。
|
324
321
|
|
325
322
|
|
323
|
+
###今後の予定
|
324
|
+
|
325
|
+
**v 0.3**
|
326
|
+
|
327
|
+
+ HTMLから取得・解析するメソッドの追加。
|
328
|
+
|
329
|
+
+ キーワード検索の実装。
|
330
|
+
|
331
|
+
**v 0.4-**
|
332
|
+
|
333
|
+
+ シリーズ性判定の強化。説明文中にある「次 sm***」等の表記を解析し、マイリストに頼らずにシリーズ性を判定するようにする。
|
334
|
+
|
335
|
+
+ コミュニティ動画、限定公開動画・マイリストへの対応。
|
336
|
+
|
337
|
+
|
326
338
|
###更新履歴
|
327
339
|
|
328
|
-
**v 0.2.
|
340
|
+
**v 0.2.10**
|
341
|
+
|
342
|
+
+ 例外発生時に終了してしまうバグを修正。
|
343
|
+
|
344
|
+
+ リクエスト成否等のステータスの詳細化。
|
345
|
+
|
346
|
+
+ パラメータの文字列による指定からシンボルによる指定への変更。
|
347
|
+
|
348
|
+
**v 0.2.8 0.2.9**
|
329
349
|
|
330
350
|
+ MylistAtomパーサが再生数等を認識しない問題を修正。
|
331
351
|
|
@@ -355,17 +375,3 @@ GitHubを経由して下さってもいいのですが、まだ慣れていな
|
|
355
375
|
|
356
376
|
+ Searcherループの継続判定を、ブロック内で`"continue"`を返す事を要求する方式に変更。
|
357
377
|
|
358
|
-
|
359
|
-
###今後の予定
|
360
|
-
|
361
|
-
**v 0.3**
|
362
|
-
|
363
|
-
+ HTMLから取得・解析するメソッドの追加。
|
364
|
-
|
365
|
-
+ キーワード検索の実装。
|
366
|
-
|
367
|
-
**v 0.4-**
|
368
|
-
|
369
|
-
+ シリーズ性判定の強化。説明文中にある「次 sm***」等の表記を解析し、マイリストに頼らずにシリーズ性を判定するようにする。
|
370
|
-
|
371
|
-
+ コミュニティ動画、限定公開動画・マイリストへの対応。
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.10
|
data/lib/classes/connector.rb
CHANGED
@@ -6,16 +6,87 @@ require 'net/http'
|
|
6
6
|
|
7
7
|
module Nicos
|
8
8
|
module Connector
|
9
|
+
|
10
|
+
#
|
11
|
+
#
|
12
|
+
#
|
13
|
+
#==ブロック内の第2引数について
|
14
|
+
#
|
15
|
+
# 第2引数には、それまでの検索の成否、例外の発生回数などを記録した
|
16
|
+
# ハッシュが渡されます。これは以下のような構造になっています。
|
17
|
+
#
|
18
|
+
# {
|
19
|
+
# # 各種例外等が発生した動画・マイリストのIDを配列で保管。
|
20
|
+
# "allDisabled" => [],
|
21
|
+
# "notPublic" => [],
|
22
|
+
# "limInCommunity" => [],
|
23
|
+
# "notFound" => [],
|
24
|
+
# "deleted" => [],
|
25
|
+
#
|
26
|
+
# # 再試行で対処できる例外等が発生した件数。
|
27
|
+
# "deniedSeqReq" => 0,
|
28
|
+
# "serverIsBusy" => 0,
|
29
|
+
# "serviceUnavailable" => 0,
|
30
|
+
# "timedOut" => 0,
|
31
|
+
#
|
32
|
+
# # 成功回数
|
33
|
+
# "succeededNum" => 0
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
# これらのパラメータが複数の結果を保持する前提になっているのは、Searcherモジュール
|
37
|
+
# に対応させる為です。将来的には、Movie/Mylistクラス
|
38
|
+
# には専用のハッシュを設ける設計にするつもりです。
|
39
|
+
#
|
40
|
+
# **allDisabled**
|
41
|
+
# マイリストの場合のみ機能。そのマイリスト内の動画が全て非公開、
|
42
|
+
# あるいは削除済み等で存在しないが、マイリストは残っている場合。
|
43
|
+
#
|
44
|
+
# **notPublic**
|
45
|
+
# 動画、マイリストが非公開である場合。
|
46
|
+
#
|
47
|
+
# **limInCommunity**
|
48
|
+
# 動画、マイリストがコミュニティ限定公開である場合。
|
49
|
+
#
|
50
|
+
# **notFound**
|
51
|
+
# 動画、マイリストが存在しない場合。マイリストは削除済みの場合もnotFoundとなる。
|
52
|
+
#
|
53
|
+
# **deleted**
|
54
|
+
# その動画が削除済みである場合。マイリストについては、上のnotFoundと
|
55
|
+
# 区別されない。
|
56
|
+
#
|
57
|
+
# **deniedSeqReq**
|
58
|
+
# 連続アクセスとして明示的に拒否された場合。
|
59
|
+
#
|
60
|
+
# **serverIsBusy**
|
61
|
+
# 「大変ご迷惑をおかけいたしますが、しばらく時間をあけてから
|
62
|
+
# 再度検索いただくようご協力をお願いいたします。」と表示される場合。
|
63
|
+
#
|
64
|
+
# **serviceUnavailable**
|
65
|
+
# 503が返ってきた時。
|
66
|
+
#
|
67
|
+
# **timedOut**
|
68
|
+
# タイムアウト
|
69
|
+
#
|
70
|
+
# **succeededNum**
|
9
71
|
class Connector < Config
|
72
|
+
include Nicos::Connector::SetWait
|
73
|
+
|
10
74
|
def initialize
|
11
75
|
# デフォルトのウェイト設定
|
12
76
|
@seqTime = 0
|
13
77
|
@result = {
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
78
|
+
:allDisabled => [],
|
79
|
+
:notPublic => [],
|
80
|
+
:limInCommunity => [],
|
81
|
+
:notFound => [],
|
82
|
+
:deleted => [],
|
83
|
+
|
84
|
+
:deniedSeqReq => 0,
|
85
|
+
:serverIsBusy => 0,
|
86
|
+
:serviceUnavailable => 0,
|
87
|
+
:timedOut => 0,
|
88
|
+
|
89
|
+
:succeededNum => 0
|
19
90
|
}
|
20
91
|
@waitConfig = @@waitConfig
|
21
92
|
end
|
@@ -23,102 +94,148 @@ module Nicos
|
|
23
94
|
attr_accessor :result
|
24
95
|
|
25
96
|
private
|
97
|
+
|
98
|
+
# 再試行しない例外の共通処理
|
99
|
+
def accessDisabled(exception)
|
100
|
+
@result[exception].push(@nowAccess)
|
101
|
+
{ :order => :skip, :status => exception }
|
102
|
+
end
|
103
|
+
|
104
|
+
def allDisabled
|
105
|
+
# MylistAtomについて、全ての動画が非公開あるいはその他の理由でRSSフィードに掲載されない時。
|
106
|
+
puts "All movies are disabled."
|
107
|
+
accessDisabled(:allDisabled)
|
108
|
+
end
|
26
109
|
|
27
110
|
def notPublic
|
28
|
-
# マイリスト非公開のときに403
|
111
|
+
# マイリスト非公開のときに403になる。
|
112
|
+
# http://www.nicovideo.jp/mylist/25479830
|
29
113
|
puts "This movie/mylist is not public."
|
30
|
-
|
31
|
-
return { "order" => "skip" }
|
114
|
+
accessDisabled(:notPublic)
|
32
115
|
end
|
33
116
|
|
34
117
|
def limInCommunity
|
35
118
|
puts "This movie/mylist is limited in comunity members."
|
36
119
|
# ex. item_id -> 1294702905
|
37
|
-
|
38
|
-
return { "order" => "skip" }
|
120
|
+
accessDisabled(:limInCommunity)
|
39
121
|
end
|
40
122
|
|
41
123
|
def notFound
|
42
124
|
puts "This movie/mylist is not found."
|
43
|
-
|
44
|
-
return { "order" => "skip" }
|
125
|
+
accessDisabled(:notFound)
|
45
126
|
end
|
46
127
|
|
47
|
-
def deleted
|
128
|
+
def deleted # マイリストは削除と404の区別がない?
|
48
129
|
puts "This movie/mylist is deleted."
|
49
|
-
|
50
|
-
|
130
|
+
accessDisabled(:deleted)
|
131
|
+
end
|
132
|
+
|
133
|
+
# 以下、再試行の可能性のある例外
|
134
|
+
|
135
|
+
# 共通処理
|
136
|
+
def exception(exception, retryCount)
|
137
|
+
if retryCount <= @waitConfig[exception][:retryLimit]
|
138
|
+
{ :order => :skip }
|
139
|
+
else
|
140
|
+
sleep @waitConfig[exception][:wait]
|
141
|
+
@result[exception] += 1
|
142
|
+
{ :order => :retry }
|
143
|
+
end
|
51
144
|
end
|
52
145
|
|
53
|
-
def deniedSeqReq
|
146
|
+
def deniedSeqReq(retryCount)
|
54
147
|
puts "Denied sequential requests."
|
55
|
-
|
56
|
-
@result = "deniedSeqReq"
|
57
|
-
return { "order" => "retry" }
|
148
|
+
exception(:deniedSeqReq, retryCount)
|
58
149
|
end
|
59
150
|
|
60
|
-
def serverIsBusy
|
151
|
+
def serverIsBusy(retryCount)
|
61
152
|
puts "The server is busy."
|
62
|
-
|
63
|
-
@result = "serverIsBusy"
|
64
|
-
return { "order" => "retry" }
|
153
|
+
exception(:serverIsBusy, retryCount)
|
65
154
|
end
|
66
155
|
|
67
|
-
def serviceUnavailable
|
156
|
+
def serviceUnavailable(retryCount)
|
68
157
|
puts "Service unavailable."
|
69
|
-
|
70
|
-
@result = "serviceUnavailable"
|
71
|
-
return { "order" => "retry" }
|
158
|
+
exception(:serviceUnavailable, retryCount)
|
72
159
|
end
|
73
160
|
|
74
|
-
def timedOut
|
161
|
+
def timedOut(retryCount)
|
75
162
|
puts "Request timed out."
|
76
|
-
|
77
|
-
@result = "timedOut"
|
78
|
-
return { "order" => "retry" }
|
163
|
+
exception(:timedOut, retryCount)
|
79
164
|
end
|
80
165
|
|
81
|
-
|
166
|
+
|
167
|
+
def reachedLast
|
168
|
+
# TagAtom専用。MylistAtomは、allDisabledと結果が被ってしまう。
|
82
169
|
puts "Reached the last page."
|
83
|
-
|
84
|
-
return { "order" => "terminate" }
|
170
|
+
{ :order => :terminate }
|
85
171
|
end
|
86
172
|
|
87
173
|
def succeeded(resBody)
|
88
|
-
@result[
|
89
|
-
sleep @waitConfig[
|
174
|
+
@result[:succeededNum] += 1
|
175
|
+
sleep @waitConfig[:each]
|
90
176
|
@seqTime += 1
|
91
177
|
|
92
|
-
if @seqTime >= @waitConfig[
|
93
|
-
sleep @waitConfig[
|
178
|
+
if @seqTime >= @waitConfig[:seqAccLimit]
|
179
|
+
sleep @waitConfig[:afterSeq]
|
94
180
|
@seqTime = 0
|
95
181
|
end
|
96
|
-
|
182
|
+
|
183
|
+
{
|
184
|
+
:status => :success,
|
185
|
+
:order => :afterTheSuccess,
|
186
|
+
:body => resBody
|
187
|
+
}
|
97
188
|
end
|
98
189
|
|
99
190
|
def wait(status)
|
100
191
|
puts "Wait for " + waitTime + " second."
|
101
192
|
sleep @waitConfig[status.to_s]
|
102
193
|
end
|
103
|
-
|
194
|
+
|
104
195
|
public
|
196
|
+
|
197
|
+
# リクエスト結果を格納する変数を、MylistやMovie等の
|
198
|
+
# 単一リクエストメソッド用に構造を変換する。
|
199
|
+
def getStatus
|
200
|
+
status = {
|
201
|
+
:status => nil,
|
202
|
+
:retry => {}
|
203
|
+
}
|
204
|
+
|
205
|
+
@result.each_key do |key|
|
206
|
+
if @result[key].instance_of?(Array) && @result[key].length >= 1
|
207
|
+
status[:status] = key
|
208
|
+
elsif key === :succeededNum && @result[key] >= 1
|
209
|
+
status[:status] = :success
|
210
|
+
elsif @result[key].instance_of?(Fixnum)
|
211
|
+
status[:retry][key] = @result[key]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
status
|
216
|
+
end
|
105
217
|
end
|
106
218
|
|
107
219
|
class Xml < Connector
|
108
220
|
def get (host, entity)
|
109
221
|
response = nil
|
222
|
+
retryCount = 0
|
223
|
+
res = {}
|
110
224
|
|
111
225
|
begin
|
112
|
-
|
226
|
+
@nowAccess = host + entity
|
227
|
+
puts "Request to " + @nowAccess
|
113
228
|
Net::HTTP.start(host, 80) { |http|
|
114
229
|
response = http.get(entity, HEADER)
|
115
230
|
}
|
116
|
-
|
231
|
+
retryCount += 1
|
117
232
|
|
118
233
|
rescue => e
|
119
234
|
puts e
|
120
235
|
rescue Timeout::Error => e
|
121
|
-
|
236
|
+
debugger
|
237
|
+
timeOut
|
238
|
+
res[:order] = :retry
|
122
239
|
|
123
240
|
else
|
124
241
|
res = case response
|
@@ -128,21 +245,22 @@ module Nicos
|
|
128
245
|
# when Net::HTTPRedirection
|
129
246
|
# fetch(response['location'], limit - 1)
|
130
247
|
when Net::HTTPForbidden
|
131
|
-
forbidden
|
248
|
+
forbidden
|
132
249
|
when Net::HTTPNotFound
|
133
250
|
notFound
|
134
251
|
when Net::HTTPServiceUnavailable
|
252
|
+
debugger
|
135
253
|
serviceUnavailable
|
136
254
|
else
|
137
255
|
unknownError
|
138
256
|
end
|
139
|
-
end until res[
|
257
|
+
end until res[:order] != :retry
|
140
258
|
|
141
259
|
res
|
142
260
|
end
|
143
261
|
end
|
144
262
|
|
145
|
-
class
|
263
|
+
class TagAtom < Xml
|
146
264
|
private
|
147
265
|
|
148
266
|
def forbidden
|
@@ -164,7 +282,19 @@ module Nicos
|
|
164
282
|
end
|
165
283
|
end
|
166
284
|
|
167
|
-
class
|
285
|
+
class MylistAtom < TagAtom
|
286
|
+
def reviewRes(resBody)
|
287
|
+
resBody = resBody.force_encoding("UTF-8")
|
288
|
+
if # アクセス集中時
|
289
|
+
/大変ご迷惑をおかけいたしますが、しばらく時間をあけてから再度検索いただくようご協力をお願いいたします。/ =~
|
290
|
+
resBody then
|
291
|
+
serverIsBusy
|
292
|
+
elsif /\<entry\>/ =~ resBody && /\<\/entry\>/ =~ resBody
|
293
|
+
succeeded(resBody)
|
294
|
+
else
|
295
|
+
allDisabled
|
296
|
+
end
|
297
|
+
end
|
168
298
|
end
|
169
299
|
|
170
300
|
class GetThumbInfo < Xml
|