rsssf 0.0.1 → 0.2.0
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.
- checksums.yaml +5 -5
- data/{HISTORY.md → CHANGELOG.md} +2 -0
- data/Manifest.txt +9 -1
- data/README.md +193 -7
- data/Rakefile +8 -7
- data/lib/rsssf/convert.rb +495 -0
- data/lib/rsssf/download.rb +151 -0
- data/lib/rsssf/page.rb +320 -0
- data/lib/rsssf/repo.rb +144 -0
- data/lib/rsssf/reports/page.rb +75 -0
- data/lib/rsssf/reports/schedule.rb +163 -0
- data/lib/rsssf/schedule.rb +21 -0
- data/lib/rsssf/utils.rb +56 -0
- data/lib/rsssf/version.rb +4 -6
- data/lib/rsssf.rb +46 -10
- metadata +32 -19
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
|
|
2
|
+
module Rsssf
|
|
3
|
+
class PageConverter
|
|
4
|
+
|
|
5
|
+
## convenience helper
|
|
6
|
+
def self.convert( html, url: )
|
|
7
|
+
@@converter ||= new ## use a "shared" built-in converter
|
|
8
|
+
@@converter.convert( html, url: url )
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
## add anchor: options or such
|
|
13
|
+
## lets you toggle adding anchors (§premier etc.) - why? why not?
|
|
14
|
+
|
|
15
|
+
def convert( html, url: )
|
|
16
|
+
### todo/fix: first check if html is all ascii-7bit e.g.
|
|
17
|
+
## includes only chars from 64 to 127!!!
|
|
18
|
+
|
|
19
|
+
## normalize newlines
|
|
20
|
+
## remove \r (form feed) used by Windows; just use \n (new line)
|
|
21
|
+
html = html.gsub( "\r", '' )
|
|
22
|
+
|
|
23
|
+
## check for html entities
|
|
24
|
+
html = html.gsub( "ä", 'ä' )
|
|
25
|
+
html = html.gsub( "ö", 'ö' )
|
|
26
|
+
html = html.gsub( "ü", 'ü' )
|
|
27
|
+
html = html.gsub( "Ä", 'Ä' )
|
|
28
|
+
html = html.gsub( "Ö", 'Ö' )
|
|
29
|
+
html = html.gsub( "Ü", 'Ü' )
|
|
30
|
+
html = html.gsub( "ß", 'ß' )
|
|
31
|
+
|
|
32
|
+
## typos / autofix - keep - why? why not?
|
|
33
|
+
html = html.gsub( "&oulm;", 'ö' ) ## support typo in entity (ö)
|
|
34
|
+
html = html.gsub( "¨", 'ü' ) ## support typo in entity (ü) - why? why not?
|
|
35
|
+
html = html.gsub( "&slig;", "ß" ) ## support typo in entity (ß)
|
|
36
|
+
html = html.gsub( "&aaacute;", "á" ) ## typo for á
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
html = html.gsub( "É", 'É' )
|
|
40
|
+
html = html.gsub( "ø", 'ø' )
|
|
41
|
+
html = html.gsub( "ã", 'ã' )
|
|
42
|
+
html = html.gsub( "õ", 'õ' )
|
|
43
|
+
html = html.gsub( "ô", 'ô' )
|
|
44
|
+
|
|
45
|
+
entities = %w[
|
|
46
|
+
À À
|
|
47
|
+
Á Á
|
|
48
|
+
 Â
|
|
49
|
+
à Ã
|
|
50
|
+
Ä Ä
|
|
51
|
+
Å Å
|
|
52
|
+
à à
|
|
53
|
+
á á
|
|
54
|
+
â â
|
|
55
|
+
ã ã
|
|
56
|
+
ä ä
|
|
57
|
+
å å
|
|
58
|
+
Æ Æ
|
|
59
|
+
æ æ
|
|
60
|
+
ß ß
|
|
61
|
+
Ç Ç
|
|
62
|
+
ç ç
|
|
63
|
+
È È
|
|
64
|
+
É É
|
|
65
|
+
Ê Ê
|
|
66
|
+
Ë Ë
|
|
67
|
+
è è
|
|
68
|
+
é é
|
|
69
|
+
ê ê
|
|
70
|
+
ë ë
|
|
71
|
+
Ì Ì
|
|
72
|
+
Í Í
|
|
73
|
+
Î Î
|
|
74
|
+
Ï Ï
|
|
75
|
+
ì ì
|
|
76
|
+
í í
|
|
77
|
+
î î
|
|
78
|
+
ï ï
|
|
79
|
+
Ñ Ñ
|
|
80
|
+
ñ ñ
|
|
81
|
+
Ò Ò
|
|
82
|
+
Ó Ó
|
|
83
|
+
Ô Ô
|
|
84
|
+
Õ Õ
|
|
85
|
+
Ö Ö
|
|
86
|
+
ò ò
|
|
87
|
+
ó ó
|
|
88
|
+
ô ô
|
|
89
|
+
õ õ
|
|
90
|
+
ö ö
|
|
91
|
+
Ø Ø
|
|
92
|
+
ø ø
|
|
93
|
+
Ù Ù
|
|
94
|
+
Ú Ú
|
|
95
|
+
Û Û
|
|
96
|
+
Ü Ü
|
|
97
|
+
ù ù
|
|
98
|
+
ú ú
|
|
99
|
+
û û
|
|
100
|
+
ü ü
|
|
101
|
+
Ý Ý
|
|
102
|
+
ý ý
|
|
103
|
+
ÿ ÿ
|
|
104
|
+
|
|
105
|
+
< <
|
|
106
|
+
> >
|
|
107
|
+
& &
|
|
108
|
+
© ©
|
|
109
|
+
® ®
|
|
110
|
+
|
|
111
|
+
Š Š
|
|
112
|
+
š š
|
|
113
|
+
č č
|
|
114
|
+
ć ć
|
|
115
|
+
Ž Ž
|
|
116
|
+
’ ’
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
entities.each_slice(2) do |str, entity|
|
|
122
|
+
html = html.gsub( entity, str )
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
##############
|
|
128
|
+
## check for more entities
|
|
129
|
+
## limit &---; to length 10 - why? why not?
|
|
130
|
+
html = html.gsub( /&[^; ]{1,10};/) do |match|
|
|
131
|
+
|
|
132
|
+
match = if match == 'ij' ## use like Van Dijk -> Van Dijk
|
|
133
|
+
'ij'
|
|
134
|
+
else
|
|
135
|
+
msg = "found unencoded html entity #{match}"
|
|
136
|
+
puts "*** WARN - #{msg}"
|
|
137
|
+
log( msg ) ## log too (see log.txt)
|
|
138
|
+
|
|
139
|
+
match ## pass through as is (1:1)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
match
|
|
143
|
+
end
|
|
144
|
+
## todo/fix: add more entities
|
|
145
|
+
|
|
146
|
+
###################################
|
|
147
|
+
### smart quotes quick fixes
|
|
148
|
+
### convert all "smart" quote to (standard) single quotes
|
|
149
|
+
## D´Alessandro => D'Alessandro
|
|
150
|
+
|
|
151
|
+
html = html.gsub( '´', "'" )
|
|
152
|
+
|
|
153
|
+
html = html.gsub( '’', "'" )
|
|
154
|
+
html = html.gsub( '‘', "'" )
|
|
155
|
+
html = html.gsub( '“', '"' )
|
|
156
|
+
html = html.gsub( '”', '"' )
|
|
157
|
+
|
|
158
|
+
### convert fancy dashes/hyphens to plain dash/hyphen
|
|
159
|
+
html = html.gsub( '–', '-' )
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
txt = html_to_txt( html )
|
|
164
|
+
|
|
165
|
+
header = <<EOS
|
|
166
|
+
<!--
|
|
167
|
+
source: #{url}
|
|
168
|
+
-->
|
|
169
|
+
|
|
170
|
+
EOS
|
|
171
|
+
|
|
172
|
+
header+txt ## return txt w/ header
|
|
173
|
+
end ## method convert
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
## todo/fix - use generic heading regex for all h2/h3/h4 etc.
|
|
177
|
+
## exclude h1 - why? why not?
|
|
178
|
+
## note - include leading and trailing spaces !!!
|
|
179
|
+
##
|
|
180
|
+
## note - for content use non-greedy to allow
|
|
181
|
+
## match of tags inside content too
|
|
182
|
+
HEADING2_RE = %r{ \s*
|
|
183
|
+
<H2>
|
|
184
|
+
(?<title>.+?)
|
|
185
|
+
</H2>
|
|
186
|
+
\s*
|
|
187
|
+
}imx
|
|
188
|
+
|
|
189
|
+
HEADING4_RE = %r{ \s*
|
|
190
|
+
<H4>
|
|
191
|
+
(?<title>.+?)
|
|
192
|
+
</H4>
|
|
193
|
+
\s*
|
|
194
|
+
}imx
|
|
195
|
+
|
|
196
|
+
def replace_h2( html )
|
|
197
|
+
html.gsub( HEADING2_RE ) do |_|
|
|
198
|
+
m = Regexp.last_match
|
|
199
|
+
puts " replace heading 2 (h2) >#{m[:title]}<"
|
|
200
|
+
"\n\n## #{m[:title]}\n\n" ## note: make sure to always add two newlines
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def replace_h4( html )
|
|
205
|
+
html.gsub( HEADING4_RE ) do |_|
|
|
206
|
+
m = Regexp.last_match
|
|
207
|
+
puts " replace heading 4 (h4) >#{m[:title]}<"
|
|
208
|
+
"\n\n#### #{m[:title]}\n\n" ## note: make sure to always add two newlines
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def squish( str )
|
|
214
|
+
## squish more than one white space to one space
|
|
215
|
+
str.gsub( /[ \r\t\n]+/, ' ' )
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def patch_about( html )
|
|
220
|
+
# <A name=about>
|
|
221
|
+
# <H2>About this document</H2></A>
|
|
222
|
+
# or
|
|
223
|
+
# <A NAME="about"><H2>About this document</H2></A>
|
|
224
|
+
# => change to (possible?)
|
|
225
|
+
# <H2><A name=about>About this document</A></H2>
|
|
226
|
+
|
|
227
|
+
html.sub( %r{<A [ ] name=(about|"about")> \s*
|
|
228
|
+
<H2>About [ ] this [ ] document</H2></A>
|
|
229
|
+
}ixm,
|
|
230
|
+
"<H2><A name=about>About this document</A></H2>"
|
|
231
|
+
)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# <a name="sa">Série A</a>
|
|
235
|
+
# <a name="sd">Série D</a>
|
|
236
|
+
|
|
237
|
+
# <A name=about>
|
|
238
|
+
# <H2>About this document</H2></A>
|
|
239
|
+
# => change to (possible?)
|
|
240
|
+
# <H2><A name=about>About this document</A></H2>
|
|
241
|
+
#
|
|
242
|
+
#
|
|
243
|
+
# <h4><a name="cb">Copa do Brasil</a></h4>
|
|
244
|
+
|
|
245
|
+
## note - for content use non-greedy to allow
|
|
246
|
+
## match of tags inside content too
|
|
247
|
+
|
|
248
|
+
A_NAME_RE = %r{<A [ ]+ NAME [ ]* =
|
|
249
|
+
(?<name>[^>]+?)
|
|
250
|
+
>
|
|
251
|
+
(?<title>.+?)
|
|
252
|
+
</A>
|
|
253
|
+
}imx
|
|
254
|
+
|
|
255
|
+
# <a href="#sa">Série A</a><br>
|
|
256
|
+
#
|
|
257
|
+
# <A href="http://www.rsssf.org/">Rec.Sport.Soccer
|
|
258
|
+
# Statistics Foundation</A>
|
|
259
|
+
# <A href="http://www.rsssfbrasil.com">RSSSF
|
|
260
|
+
# Brazil</A>
|
|
261
|
+
#
|
|
262
|
+
# and Daniel Dalence (<A
|
|
263
|
+
# href="mailto:danielballack@terra.com.br">danielballack@terra.com.br</A>)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
A_HREF_RE = %r{<A \s+ HREF [ ]* =
|
|
267
|
+
(?<href>[^>]+?)
|
|
268
|
+
>
|
|
269
|
+
(?<title>.+?)
|
|
270
|
+
<\/A>
|
|
271
|
+
}imx
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def replace_a_href( html )
|
|
275
|
+
## remove anchors (a href)
|
|
276
|
+
# note: heading 4 includes anchor (thus, let anchors go first)
|
|
277
|
+
# note: <a \newline href is used for authors email - thus incl. support for newline as space
|
|
278
|
+
html.gsub( A_HREF_RE ) do |match| ## note: use .+? non-greedy match
|
|
279
|
+
m = Regexp.last_match
|
|
280
|
+
href = m[:href].gsub( /["']/, '' ).strip ## remove ("" or '')
|
|
281
|
+
title = m[:title].strip ## note: "save" caputure first; gets replaced by gsub (next regex call)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
## e.g.
|
|
285
|
+
## ‹Larsen23@gmx.de, see page mailto:Larsen23@gmx.de›
|
|
286
|
+
## ‹danielballack@terra.com.br, see page mailto:danielballack@terra.com.br›
|
|
287
|
+
## ‹zja70@aol.com, see page mailto:zja70@aol.com›)
|
|
288
|
+
if href.start_with?( 'mailto:')
|
|
289
|
+
puts " blank mailto - anchor (a) href >#{href}, >#{title}<"
|
|
290
|
+
'‹mailto›' ## delete/remove email
|
|
291
|
+
else
|
|
292
|
+
puts " replace anchor (a) href >#{href}, >#{title}<"
|
|
293
|
+
|
|
294
|
+
## convert href to xref
|
|
295
|
+
xref = if href.start_with?('#') ## in-page ref
|
|
296
|
+
", see §#{href[1..-1]}"
|
|
297
|
+
elsif href.start_with?( /https?:/ ) ## external page ref
|
|
298
|
+
## skip - keep empty - why? why not? (or add url domain?)
|
|
299
|
+
''
|
|
300
|
+
else
|
|
301
|
+
## hack - check for some custom excludes
|
|
302
|
+
if title.start_with?( 'Rec.Sport.Soccer' )
|
|
303
|
+
## skip - keep empty
|
|
304
|
+
''
|
|
305
|
+
else
|
|
306
|
+
## strip (ending) .htm|html
|
|
307
|
+
", see page #{href.sub( /\.html?$/,'')}"
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
"‹#{squish(title)}#{xref}›"
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def replace_a_name( html )
|
|
317
|
+
##
|
|
318
|
+
## remove (named) anchors
|
|
319
|
+
html.gsub( A_NAME_RE ) do |match| ## note: use .+? non-greedy match
|
|
320
|
+
m = Regexp.last_match
|
|
321
|
+
name = m[:name].gsub( /["']/, '' ).strip ## remove ("" or '')
|
|
322
|
+
title = m[:title].strip ## note: "save" caputure first; gets replaced by gsub (next regex call)
|
|
323
|
+
match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
|
|
324
|
+
puts " replace anchor (a) name >#{name}<, >#{title}< - >#{match}<"
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
##
|
|
328
|
+
## todo - report WARN if title incl. tags
|
|
329
|
+
## assumes text only for now - why? why not?
|
|
330
|
+
## add a name inside heading !!!
|
|
331
|
+
## do NOT add heading inside a name !!!
|
|
332
|
+
|
|
333
|
+
"#{title} ‹§#{name}›" ## note - use two spaces min (between title & name)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
EMAIL_RE = %r{ \s*
|
|
339
|
+
\(
|
|
340
|
+
[a-z][a-z0-9_]+
|
|
341
|
+
@[a-z]+(\.[a-z]+)+
|
|
342
|
+
\)
|
|
343
|
+
}imx
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def remove_emails( html )
|
|
347
|
+
### remove converted ("blineded") mailto anchors
|
|
348
|
+
## note usually inside () e.g.
|
|
349
|
+
## (‹mailto›)
|
|
350
|
+
## plus slurp up all leading whitespace (incl. newline) - why? why not?
|
|
351
|
+
html = html.gsub( /\s*
|
|
352
|
+
\(‹mailto›\)
|
|
353
|
+
/xm, '' )
|
|
354
|
+
|
|
355
|
+
###
|
|
356
|
+
## remove "regular emails too e.g.
|
|
357
|
+
##
|
|
358
|
+
## Thanks to Marcelo Leme de Arruda (___@___.__.br),
|
|
359
|
+
## Ricardo FF Pontes (___@____.com),
|
|
360
|
+
## Santiago Reis (____@____.com.br),
|
|
361
|
+
## Marcos Lacerda Queiroz (___@____.com.br)
|
|
362
|
+
## etc.
|
|
363
|
+
|
|
364
|
+
## check for "free-standing e.g. on its own line" emails only for now
|
|
365
|
+
html = html.gsub( EMAIL_RE ) do |match|
|
|
366
|
+
puts "removing email >#{match}<"
|
|
367
|
+
''
|
|
368
|
+
end
|
|
369
|
+
html
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def html_to_txt( html )
|
|
375
|
+
|
|
376
|
+
###
|
|
377
|
+
# todo: check if any tags (still) present??
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
## cut off everything before body
|
|
381
|
+
html = html.sub( /.+?<BODY>\s*/im, '' )
|
|
382
|
+
|
|
383
|
+
## cut off everything after body (closing)
|
|
384
|
+
html = html.sub( /<\/BODY>.*/im, '' )
|
|
385
|
+
|
|
386
|
+
html = patch_about( html )
|
|
387
|
+
|
|
388
|
+
## remove cite
|
|
389
|
+
html = html.gsub( /<CITE>([^<]+)<\/CITE>/im ) do |_|
|
|
390
|
+
puts " remove cite >#{$1}<"
|
|
391
|
+
"#{$1}"
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
html = html.gsub( /\s*<HR>\s*/im ) do |match|
|
|
395
|
+
match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
|
|
396
|
+
puts " replace horizontal rule (hr) - >#{match}<"
|
|
397
|
+
"\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n" ## check what hr to use use - . - . - or =-=-=-= or somehting distinct?
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
## replace break (br)
|
|
401
|
+
## note: do NOT use m/multiline for now - why? why not??
|
|
402
|
+
html = html.gsub( /<BR>\s*/i ) do |match| ## note: include (swallow) "extra" newline
|
|
403
|
+
match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
|
|
404
|
+
puts " replace break (br) - >#{match}<"
|
|
405
|
+
"\n"
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
html = replace_a_href( html )
|
|
411
|
+
## note a name="about" includes more a hrefs etc.
|
|
412
|
+
# let it go first (before a href)
|
|
413
|
+
html = replace_a_name( html )
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
## replace paragrah (p)
|
|
418
|
+
html = html.gsub( /\s*<P>\s*/im ) do |match| ## note: include (swallow) "extra" newline
|
|
419
|
+
match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
|
|
420
|
+
puts " replace paragraph (p) - >#{match}<"
|
|
421
|
+
"\n\n"
|
|
422
|
+
end
|
|
423
|
+
html = html.gsub( /<\/P>/i, '' ) ## replace paragraph (p) closing w/ nothing for now
|
|
424
|
+
|
|
425
|
+
## remove i
|
|
426
|
+
html = html.gsub( /<I>([^<]+)<\/I>/im ) do |_|
|
|
427
|
+
puts " remove italic (i) >#{$1}<"
|
|
428
|
+
"#{$1}"
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
html = replace_h2( html )
|
|
433
|
+
html = replace_h4( html )
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
## remove b - note: might include anchors (thus, call after anchors)
|
|
439
|
+
html = html.gsub( /<B>([^<]+)<\/B>/im ) do |_|
|
|
440
|
+
puts " remove bold (b) >#{$1}<"
|
|
441
|
+
"**#{$1}**"
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
## replace preformatted (pre)
|
|
445
|
+
html = html.gsub( /<PRE>|<\/PRE>/i ) do |_|
|
|
446
|
+
puts " replace preformatted (pre)"
|
|
447
|
+
'' # replace w/ nothing for now (keep surrounding newlines)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
=begin
|
|
451
|
+
puts
|
|
452
|
+
puts
|
|
453
|
+
puts "html:"
|
|
454
|
+
puts html[0..2000]
|
|
455
|
+
puts "-- snip --"
|
|
456
|
+
puts html[-1000..-1] ## print last hundred chars
|
|
457
|
+
=end
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
html = remove_emails( html )
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
## cleanup whitespaces
|
|
464
|
+
## todo/fix: convert newline in space first
|
|
465
|
+
## and than collapse spaces etc.!!!
|
|
466
|
+
txt = String.new
|
|
467
|
+
html.each_line do |line|
|
|
468
|
+
line = line.gsub( "\t", ' ' ) # replace all tabs w/ two spaces for nwo
|
|
469
|
+
line = line.rstrip # remove trailing whitespace (incl. newline/formfeed)
|
|
470
|
+
|
|
471
|
+
txt << line
|
|
472
|
+
txt << "\n"
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
txt
|
|
476
|
+
end # method html_to_text
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
###
|
|
481
|
+
# more helpers
|
|
482
|
+
def log( msg )
|
|
483
|
+
## append msg to ./logs.txt
|
|
484
|
+
## use ./errors.txt - why? why not?
|
|
485
|
+
File.open( './logs.txt', 'a:utf-8' ) do |f|
|
|
486
|
+
f.write( msg )
|
|
487
|
+
f.write( "\n" )
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
end # module PageConverter
|
|
494
|
+
end # module Rsssf
|
|
495
|
+
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
|
|
2
|
+
module Rsssf
|
|
3
|
+
|
|
4
|
+
## end_year to slug_year
|
|
5
|
+
## check if generic rule/convention in use ???
|
|
6
|
+
## 2007-08: tablesd/duit08.html
|
|
7
|
+
## 2008-09: tablesd/duit09.html
|
|
8
|
+
## 2009-10: tablesd/duit2010.html
|
|
9
|
+
## 2010-11: tablesd/duit2011.html
|
|
10
|
+
## 2011-12: tablesd/duit2012.html
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## map country codes to table pages
|
|
14
|
+
## add options about (char) encoding ??? - why? why not?
|
|
15
|
+
TABLE = {
|
|
16
|
+
'eng' => ['tablese/eng{year}', { encoding: 'Windows-1252' } ],
|
|
17
|
+
'es' => ['tabless/span{year}', { encoding: 'Windows-1252' } ],
|
|
18
|
+
'de' => ['tablesd/duit{year}', { encoding: 'Windows-1252' } ],
|
|
19
|
+
'at' => ['tableso/oost{year}', { encoding: 'Windows-1252' } ],
|
|
20
|
+
'br' => [
|
|
21
|
+
->(season) {
|
|
22
|
+
## note: special slug/case for year/season 2000
|
|
23
|
+
## see rsssf.org/tablesb/brazchamp.html
|
|
24
|
+
if season == Season('2000')
|
|
25
|
+
'tablesb/braz-joao{year}' ## use braz-joao00 - why? why not?
|
|
26
|
+
else
|
|
27
|
+
'tablesb/braz{year}'
|
|
28
|
+
end
|
|
29
|
+
}, { encoding: 'Windows-1252' } ],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
BASE_URL = "https://rsssf.org"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def self.table_url( code, season: )
|
|
37
|
+
url, _ = table_url_and_encoding( code, season: season )
|
|
38
|
+
url
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.table_url_and_encoding( code, season: )
|
|
42
|
+
season = Season( season )
|
|
43
|
+
|
|
44
|
+
table = TABLE[ code.downcase ]
|
|
45
|
+
tmpl = table[0]
|
|
46
|
+
tmpl = tmpl.call( season ) if tmpl.is_a?(Proc) ## check for proc
|
|
47
|
+
|
|
48
|
+
opts = table[1] || {}
|
|
49
|
+
encoding = opts[:encoding] || 'UTF-8'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
slug = if season.end_year < 2010 ## cut off all digits (only keep last two)s
|
|
53
|
+
## convert end_year to string with leading zero
|
|
54
|
+
'%02d' % (season.end_year % 100) ## e.g. 00 / 01 / 99 / 98 / 11 / etc.
|
|
55
|
+
else
|
|
56
|
+
'%4d' % season.end_year
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
tmpl = tmpl.sub( '{year}', slug )
|
|
60
|
+
url = "#{BASE_URL}/#{tmpl}.html"
|
|
61
|
+
|
|
62
|
+
[url, encoding]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def self.download_table( code, season: )
|
|
67
|
+
url, encoding = table_url_and_encoding( code, season: season )
|
|
68
|
+
|
|
69
|
+
download_page( url, encoding: encoding )
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def self.download_page( url, encoding: )
|
|
74
|
+
|
|
75
|
+
## note: assume plain 7-bit ascii for now
|
|
76
|
+
## -- assume rsssf uses ISO_8859_15 (updated version of ISO_8859_1)
|
|
77
|
+
###-- does NOT use utf-8 character encoding!!!
|
|
78
|
+
response = Webget.page( url, encoding: encoding ) ## fetch (and cache) html page (via HTTP GET)
|
|
79
|
+
|
|
80
|
+
## note: exit on get / fetch error - do NOT continue for now - why? why not?
|
|
81
|
+
exit 1 if response.status.nok? ## e.g. HTTP status code != 200
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
puts "html:"
|
|
85
|
+
html = response.text( encoding: encoding )
|
|
86
|
+
pp html[0..400]
|
|
87
|
+
html
|
|
88
|
+
end
|
|
89
|
+
end # module Rsssf
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
__END__
|
|
94
|
+
|
|
95
|
+
1998-99: tablesd/duit99.html
|
|
96
|
+
1999-00: tablesd/duit00.html ## use 1999-2000 - why?? why not??
|
|
97
|
+
2000-01: tablesd/duit01.html
|
|
98
|
+
2001-02: tablesd/duit02.html
|
|
99
|
+
2002-03: tablesd/duit03.html
|
|
100
|
+
2003-04: tablesd/duit04.html
|
|
101
|
+
2004-05: tablesd/duit05.html
|
|
102
|
+
2005-06: tablesd/duit06.html
|
|
103
|
+
2006-07: tablesd/duit07.html
|
|
104
|
+
2007-08: tablesd/duit08.html
|
|
105
|
+
2008-09: tablesd/duit09.html
|
|
106
|
+
2009-10: tablesd/duit2010.html
|
|
107
|
+
2010-11: tablesd/duit2011.html
|
|
108
|
+
2011-12: tablesd/duit2012.html
|
|
109
|
+
2012-13: tablesd/duit2013.html
|
|
110
|
+
2013-14: tablesd/duit2014.html
|
|
111
|
+
2014-15: tablesd/duit2015.html
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
2010-11: tableso/oost2011.html
|
|
115
|
+
2011-12: tableso/oost2012.html
|
|
116
|
+
2012-13: tableso/oost2013.html
|
|
117
|
+
2013-14: tableso/oost2014.html
|
|
118
|
+
2014-15: tableso/oost2015.html
|
|
119
|
+
2015-16: tableso/oost2016.html
|
|
120
|
+
|
|
121
|
+
2011: tablesb/braz2011.html !! Windows-1252
|
|
122
|
+
2012: tablesb/braz2012.html !! Windows-1252
|
|
123
|
+
2013: tablesb/braz2013.html !! Windows-1252
|
|
124
|
+
2014: tablesb/braz2014.html !! Windows-1252
|
|
125
|
+
2015: tablesb/braz2015.html !! Windows-1252
|
|
126
|
+
2016: tablesb/braz2016.html !! Windows-1252
|
|
127
|
+
2017: tablesb/braz2017.html !! Windows-1252
|
|
128
|
+
2018: tablesb/braz2018.html !! Windows-1252
|
|
129
|
+
2019: tablesb/braz2019.html !! Windows-1252
|
|
130
|
+
2020: tablesb/braz2020.html !! Windows-1252 ## 2020/21 - extended for corona
|
|
131
|
+
2021: tablesb/braz2021.html !! Windows-1252
|
|
132
|
+
2022: tablesb/braz2022.html !! Windows-1252
|
|
133
|
+
2023: tablesb/braz2023.html !! Windows-1252
|
|
134
|
+
2024: tablesb/braz2024.html !! Windows-1252
|
|
135
|
+
|
|
136
|
+
2010-11: tablese/eng2011.html !! Windows-1252
|
|
137
|
+
2011-12: tablese/eng2012.html !! Windows-1252
|
|
138
|
+
2012-13: tablese/eng2013.html !! Windows-1252
|
|
139
|
+
2013-14: tablese/eng2014.html !! Windows-1252
|
|
140
|
+
2014-15: tablese/eng2015.html !! Windows-1252
|
|
141
|
+
2015-16: tablese/eng2016.html !! Windows-1252
|
|
142
|
+
2016-17: tablese/eng2017.html !! Windows-1252
|
|
143
|
+
2017-18: tablese/eng2018.html !! Windows-1252
|
|
144
|
+
2018-19: tablese/eng2019.html !! Windows-1252
|
|
145
|
+
2019-20: tablese/eng2020.html !! Windows-1252
|
|
146
|
+
2020-21: tablese/eng2021.html !! Windows-1252
|
|
147
|
+
2021-22: tablese/eng2022.html !! Windows-1252
|
|
148
|
+
2022-23: tablese/eng2023.html !! Windows-1252
|
|
149
|
+
2023-24: tablese/eng2024.html !! Windows-1252
|
|
150
|
+
|
|
151
|
+
|