apollo-crawler 0.1.5 → 0.1.6
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 +8 -8
- data/bin/apollo-crawler +12 -410
- data/lib/apollo_crawler.rb +31 -20
- data/lib/apollo_crawler/{cache.rb → cache/cache_base.rb} +37 -34
- data/lib/apollo_crawler/cache/factory.rb +35 -0
- data/lib/apollo_crawler/{caches → cache}/filesystem_cache.rb +37 -34
- data/lib/apollo_crawler/cache/memcached_cache.rb +51 -0
- data/lib/apollo_crawler/{caches → cache}/memory_cache.rb +46 -43
- data/lib/apollo_crawler/{caches → cache}/null_cache.rb +33 -30
- data/lib/apollo_crawler/config.rb +53 -0
- data/lib/apollo_crawler/{crawler.rb → crawler/crawler_base.rb} +157 -155
- data/lib/apollo_crawler/{crawler_template.rb → crawler/crawler_template.rb} +24 -24
- data/lib/apollo_crawler/{crawlers → crawler}/google_com/google.rb +40 -40
- data/lib/apollo_crawler/{crawlers → crawler}/slashdot_org/slashdot.rb +40 -40
- data/lib/apollo_crawler/{crawlers → crawler}/stackoverflow_com/stackoverflow.rb +44 -44
- data/lib/apollo_crawler/{crawlers → crawler}/xkcd_com/xkcd.rb +35 -35
- data/lib/apollo_crawler/{crawlers → crawler}/ycombinator_com/hacker_news.rb +44 -44
- data/lib/apollo_crawler/fetcher/fetcher_base.rb +6 -0
- data/lib/apollo_crawler/fetcher/simple_fetcher.rb +8 -0
- data/lib/apollo_crawler/formatter/formatter_base.rb +6 -0
- data/lib/apollo_crawler/{formatters → formatter}/formatter_json.rb +17 -17
- data/lib/apollo_crawler/{formatters → formatter}/formatter_plain.rb +17 -17
- data/lib/apollo_crawler/{formatters → formatter}/formatter_table.rb +35 -35
- data/lib/apollo_crawler/lib.rb +28 -0
- data/lib/apollo_crawler/program.rb +406 -0
- data/lib/apollo_crawler/store/store_base.rb +6 -0
- data/lib/apollo_crawler/version.rb +2 -2
- metadata +52 -17
- data/lib/apollo_crawler/caches/factory.rb +0 -30
- data/lib/apollo_crawler/formatter.rb +0 -6
@@ -1,34 +1,37 @@
|
|
1
|
-
module Apollo
|
2
|
-
module
|
3
|
-
class
|
4
|
-
# Get value associated with key from cache
|
5
|
-
def get(key, *args)
|
6
|
-
|
7
|
-
# Not found, Create, cache and return
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
1
|
+
module Apollo
|
2
|
+
module Cache
|
3
|
+
class CacheBase
|
4
|
+
# Get value associated with key from cache
|
5
|
+
def get(key, *args)
|
6
|
+
|
7
|
+
# Not found, Create, cache and return
|
8
|
+
if block_given?
|
9
|
+
res = yield args
|
10
|
+
end
|
11
|
+
|
12
|
+
return res
|
13
|
+
end
|
14
|
+
|
15
|
+
# Set value associated with key
|
16
|
+
# Return cached value
|
17
|
+
def set(key, value)
|
18
|
+
return value
|
19
|
+
end
|
20
|
+
|
21
|
+
# Check if cache contains specified key
|
22
|
+
def contains(key)
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
# Invalidate key/value pair
|
27
|
+
def invalidate(key)
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Clear cache
|
32
|
+
def clear
|
33
|
+
return
|
34
|
+
end
|
35
|
+
end # CacheBase
|
36
|
+
end # Cache
|
37
|
+
end # Apollo
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Global config file
|
2
|
+
require File.join(File.dirname(__FILE__), '..', 'config')
|
3
|
+
|
4
|
+
# Cache instance base class
|
5
|
+
require File.join(File.dirname(__FILE__), 'cache_base')
|
6
|
+
|
7
|
+
# Factory uses singleton pattern
|
8
|
+
require 'singleton'
|
9
|
+
|
10
|
+
module Apollo
|
11
|
+
module Cache
|
12
|
+
class Factory
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@cache = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.construct()
|
20
|
+
self.singleton.construct()
|
21
|
+
end
|
22
|
+
|
23
|
+
def construct()
|
24
|
+
if(@cache.nil? == false)
|
25
|
+
return @cache
|
26
|
+
end
|
27
|
+
|
28
|
+
res = RbConfig::CACHE_CLASS.new
|
29
|
+
|
30
|
+
@cache = res
|
31
|
+
return res
|
32
|
+
end
|
33
|
+
end # Factory
|
34
|
+
end # Cache
|
35
|
+
end # Apollo
|
@@ -1,34 +1,37 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '
|
2
|
-
|
3
|
-
module Apollo
|
4
|
-
module
|
5
|
-
class Filesystem <
|
6
|
-
def initialize
|
7
|
-
# puts "This if Filesystem cache"
|
8
|
-
end
|
9
|
-
|
10
|
-
# Get value associated with key from cache
|
11
|
-
def get(key, *args)
|
12
|
-
# Not found, Create, cache and return
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
1
|
+
require File.join(File.dirname(__FILE__), 'cache_base')
|
2
|
+
|
3
|
+
module Apollo
|
4
|
+
module Cache
|
5
|
+
class Filesystem < CacheBase
|
6
|
+
def initialize
|
7
|
+
# puts "This if Filesystem cache"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Get value associated with key from cache
|
11
|
+
def get(key, *args)
|
12
|
+
# Not found, Create, cache and return
|
13
|
+
if block_given?
|
14
|
+
res = yield args
|
15
|
+
end
|
16
|
+
|
17
|
+
return res
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set value associated with key
|
21
|
+
# Return cached value
|
22
|
+
def set(key, value)
|
23
|
+
return value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check if cache contains specified key
|
27
|
+
def contains(key)
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Invalidate key/value pair
|
32
|
+
def invalidate(key)
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
end # Filesystem
|
36
|
+
end # Cache
|
37
|
+
end # Apollo
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'cache_base')
|
2
|
+
|
3
|
+
require 'dalli'
|
4
|
+
|
5
|
+
module Apollo
|
6
|
+
module Cache
|
7
|
+
class Memcached < CacheBase
|
8
|
+
@cache = nil
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@cache = Dalli::Client.new()
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get value associated with key from cache
|
15
|
+
def get(key, *args)
|
16
|
+
res = @cache.get(key)
|
17
|
+
|
18
|
+
# Not found, Create, cache and return
|
19
|
+
if res.nil? && block_given?
|
20
|
+
res = yield args
|
21
|
+
|
22
|
+
self.set(key, res)
|
23
|
+
end
|
24
|
+
|
25
|
+
return res
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set value associated with key
|
29
|
+
# Return cached value
|
30
|
+
def set(key, value)
|
31
|
+
@cache.set(key, value)
|
32
|
+
return key
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check if cache contains specified key
|
36
|
+
def contains(key)
|
37
|
+
# TODO: Implement
|
38
|
+
end
|
39
|
+
|
40
|
+
# Invalidate key/value pair
|
41
|
+
def invalidate(key)
|
42
|
+
# TODO: Implement
|
43
|
+
end
|
44
|
+
|
45
|
+
# Clear cache
|
46
|
+
def clear
|
47
|
+
# TODO: Implement
|
48
|
+
end
|
49
|
+
end # Null
|
50
|
+
end # Cache
|
51
|
+
end # Apollo
|
@@ -1,43 +1,46 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '
|
2
|
-
|
3
|
-
module Apollo
|
4
|
-
module
|
5
|
-
class Memory <
|
6
|
-
@
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@
|
10
|
-
end
|
11
|
-
|
12
|
-
# Get value associated with key from cache
|
13
|
-
def get(key, *args)
|
14
|
-
@
|
15
|
-
|
16
|
-
# Not found, Create, cache and return
|
17
|
-
res
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
1
|
+
require File.join(File.dirname(__FILE__), 'cache_base')
|
2
|
+
|
3
|
+
module Apollo
|
4
|
+
module Cache
|
5
|
+
class Memory < CacheBase
|
6
|
+
@cache = nil
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@cache = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get value associated with key from cache
|
13
|
+
def get(key, *args)
|
14
|
+
res = @cache[key]
|
15
|
+
|
16
|
+
# Not found, Create, cache and return
|
17
|
+
if res.nil? && block_given?
|
18
|
+
res = yield args
|
19
|
+
end
|
20
|
+
|
21
|
+
return res
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set value associated with key
|
25
|
+
# Return cached value
|
26
|
+
def set(key, value)
|
27
|
+
@cache[key] = value
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if cache contains specified key
|
31
|
+
def contains(key)
|
32
|
+
@cache.has_key?(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Invalidate key/value pair
|
36
|
+
def invalidate(key)
|
37
|
+
@cache.delete(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Clear cache
|
41
|
+
def clear
|
42
|
+
@cache.clear
|
43
|
+
end
|
44
|
+
end # Null
|
45
|
+
end # Cache
|
46
|
+
end # Apollo
|
@@ -1,30 +1,33 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '
|
2
|
-
|
3
|
-
module Apollo
|
4
|
-
module
|
5
|
-
class Null <
|
6
|
-
# Get value associated with key from cache
|
7
|
-
def get(key, *args)
|
8
|
-
# Not found, Create, cache and return
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
1
|
+
require File.join(File.dirname(__FILE__), 'cache_base')
|
2
|
+
|
3
|
+
module Apollo
|
4
|
+
module Cache
|
5
|
+
class Null < CacheBase
|
6
|
+
# Get value associated with key from cache
|
7
|
+
def get(key, *args)
|
8
|
+
# Not found, Create, cache and return
|
9
|
+
if block_given?
|
10
|
+
res = yield args
|
11
|
+
end
|
12
|
+
|
13
|
+
return res
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set value associated with key
|
17
|
+
# Return cached value
|
18
|
+
def set(key, value)
|
19
|
+
return value
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check if cache contains specified key
|
23
|
+
def contains(key)
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
|
27
|
+
# Invalidate key/value pair
|
28
|
+
def invalidate(key)
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
end # Null
|
32
|
+
end # Cache
|
33
|
+
end # Apollo
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Caches
|
2
|
+
require File.join(File.dirname(__FILE__), 'lib')
|
3
|
+
|
4
|
+
module RbConfig
|
5
|
+
############################################################
|
6
|
+
# Caches - caches implementations
|
7
|
+
############################################################
|
8
|
+
CACHES_DIR = File.join(File.dirname(__FILE__), "caches")
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
############################################################
|
13
|
+
# Cache implementation used for chaching pages retreived
|
14
|
+
############################################################
|
15
|
+
#
|
16
|
+
# Filesystem backend
|
17
|
+
# CACHE_CLASS = Apollo::Cache::Filesystem
|
18
|
+
#
|
19
|
+
# Memcached - expects localhost:11211
|
20
|
+
# CACHE_CLASS = Apollo::Cache::Memcached
|
21
|
+
#
|
22
|
+
# Pure naive ruby in-memory implementation
|
23
|
+
# CACHE_CLASS = Apollo::Cache::Memory
|
24
|
+
#
|
25
|
+
# Null caching - no caching at all
|
26
|
+
# CACHE_CLASS = Apollo::Cache::Null
|
27
|
+
|
28
|
+
# Used caching mechanism by default
|
29
|
+
CACHE_CLASS = Apollo::Cache::Memcached
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
############################################################
|
34
|
+
# Crawlers - Built-in out-of box working crawlers
|
35
|
+
############################################################
|
36
|
+
CRAWLERS_DIR = File.join(File.dirname(__FILE__), "crawlers")
|
37
|
+
|
38
|
+
# Template used for generated crawlers
|
39
|
+
CRAWLER_TEMPLATE_NAME = "crawler_template.rb"
|
40
|
+
|
41
|
+
# Path of template
|
42
|
+
CRAWLER_TEMPLATE_PATH = File.join(File.dirname(__FILE__), "crawler_template.rb")
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
############################################################
|
47
|
+
# Formatters - used for formatting crawled documents results
|
48
|
+
############################################################
|
49
|
+
FORMATTERS_DIR = File.join(File.dirname(__FILE__), "formatters")
|
50
|
+
|
51
|
+
# Default formatter if no other specified
|
52
|
+
DEFAULT_FORMATTER = Apollo::Formatter::Json
|
53
|
+
end # Config
|
@@ -1,155 +1,157 @@
|
|
1
|
-
require "open-uri"
|
2
|
-
require "nokogiri"
|
3
|
-
|
4
|
-
module Apollo
|
5
|
-
module
|
6
|
-
class
|
7
|
-
@backlog = nil
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@backlog = []
|
11
|
-
end
|
12
|
-
|
13
|
-
# Name of the crawler
|
14
|
-
def name
|
15
|
-
return "Crawler Base"
|
16
|
-
end
|
17
|
-
|
18
|
-
def url
|
19
|
-
return nil
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.try_get_url(root, url)
|
23
|
-
begin
|
24
|
-
return URI.join(root, url)
|
25
|
-
rescue
|
26
|
-
return nil
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# - (0) Figure out URL
|
31
|
-
# - (1) Extract Data
|
32
|
-
# - (2) Extract Links
|
33
|
-
# - (3) Go to (0) eventually
|
34
|
-
def etl(url=nil, &block)
|
35
|
-
# Look for passed URL use default instead and fail if it is not valid
|
36
|
-
if(url.nil? || url.empty?)
|
37
|
-
url = self.url
|
38
|
-
end
|
39
|
-
|
40
|
-
if(url.nil?)
|
41
|
-
return nil
|
42
|
-
end
|
43
|
-
|
44
|
-
if(url.kind_of?(Array))
|
45
|
-
@backlog.concat(url)
|
46
|
-
else
|
47
|
-
@backlog << url
|
48
|
-
end
|
49
|
-
|
50
|
-
res = []
|
51
|
-
# TODO: Respect limit of documents/urls processed
|
52
|
-
while(@backlog.empty? == false)
|
53
|
-
url = @backlog.shift
|
54
|
-
|
55
|
-
# puts "Processing '#{url}'"
|
56
|
-
doc = self.process_url(url)
|
57
|
-
res << doc
|
58
|
-
|
59
|
-
# TODO: Use log4r and log it only on info level
|
60
|
-
# TODO: Add some async/callback signal for document processed
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
1
|
+
require "open-uri"
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
module Apollo
|
5
|
+
module Crawler
|
6
|
+
class CrawlerBase
|
7
|
+
@backlog = nil
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@backlog = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Name of the crawler
|
14
|
+
def name
|
15
|
+
return "Crawler Base"
|
16
|
+
end
|
17
|
+
|
18
|
+
def url
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.try_get_url(root, url)
|
23
|
+
begin
|
24
|
+
return URI.join(root, url)
|
25
|
+
rescue
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# - (0) Figure out URL
|
31
|
+
# - (1) Extract Data
|
32
|
+
# - (2) Extract Links
|
33
|
+
# - (3) Go to (0) eventually
|
34
|
+
def etl(url=nil, &block)
|
35
|
+
# Look for passed URL use default instead and fail if it is not valid
|
36
|
+
if(url.nil? || url.empty?)
|
37
|
+
url = self.url
|
38
|
+
end
|
39
|
+
|
40
|
+
if(url.nil?)
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
if(url.kind_of?(Array))
|
45
|
+
@backlog.concat(url)
|
46
|
+
else
|
47
|
+
@backlog << url
|
48
|
+
end
|
49
|
+
|
50
|
+
res = []
|
51
|
+
# TODO: Respect limit of documents/urls processed
|
52
|
+
while(@backlog.empty? == false)
|
53
|
+
url = @backlog.shift
|
54
|
+
|
55
|
+
# puts "Processing '#{url}'"
|
56
|
+
doc = self.process_url(url)
|
57
|
+
res << doc
|
58
|
+
|
59
|
+
# TODO: Use log4r and log it only on info level
|
60
|
+
# TODO: Add some async/callback signal for document processed
|
61
|
+
if block_given?
|
62
|
+
yield res
|
63
|
+
end
|
64
|
+
|
65
|
+
if(!doc.nil? && !doc.empty?)
|
66
|
+
doc[:links].each do |link|
|
67
|
+
url = link[:link].to_s
|
68
|
+
# TODO: Use log4r and log it only on info level
|
69
|
+
#puts url
|
70
|
+
|
71
|
+
# TODO: Check if it is unique
|
72
|
+
@backlog << url
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return res
|
77
|
+
end
|
78
|
+
|
79
|
+
def process_url(url)
|
80
|
+
# Try fetch document
|
81
|
+
doc = self.fetch_document(url)
|
82
|
+
if(doc.nil?)
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Try extract data from document
|
87
|
+
data = self.extract_data(doc)
|
88
|
+
|
89
|
+
# Try extract links for another documents
|
90
|
+
links = self.extract_links(doc)
|
91
|
+
puts links.inspect
|
92
|
+
|
93
|
+
# Format ETL result
|
94
|
+
res = {
|
95
|
+
:crawler => self.class.name,
|
96
|
+
:title => doc.title,
|
97
|
+
:data => data,
|
98
|
+
:links => links
|
99
|
+
}
|
100
|
+
|
101
|
+
return res
|
102
|
+
end
|
103
|
+
|
104
|
+
# Fetch document
|
105
|
+
def fetch_document(url)
|
106
|
+
# TODO: Refactor following idiom
|
107
|
+
if(url == nil)
|
108
|
+
url = self.url
|
109
|
+
end
|
110
|
+
|
111
|
+
if(url.nil?)
|
112
|
+
return nil
|
113
|
+
end
|
114
|
+
|
115
|
+
# TODO: Use some (custom-made) low-level HTTTP Protocol cache - just for sure
|
116
|
+
cache = Apollo::Cache::Factory.instance.construct
|
117
|
+
raw = cache.get(url) do
|
118
|
+
max_attempts = 3
|
119
|
+
attempt_no = 0
|
120
|
+
success = false
|
121
|
+
|
122
|
+
res = nil
|
123
|
+
while(attempt_no < max_attempts && success == false) do
|
124
|
+
begin
|
125
|
+
res = open(url).read
|
126
|
+
success = true
|
127
|
+
rescue Exception => e
|
128
|
+
puts "EXCEPTION: Unable to fetch '#{url}', reason: '#{e.to_s}'"
|
129
|
+
sleep 1
|
130
|
+
|
131
|
+
attempt_no = attempt_no + 1
|
132
|
+
success = false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
res
|
137
|
+
end
|
138
|
+
|
139
|
+
# TODO: Encapsulate and make more robust => invalid hostname, timeouts and so
|
140
|
+
doc = Nokogiri::HTML(raw)
|
141
|
+
return doc
|
142
|
+
end
|
143
|
+
|
144
|
+
# Extracts data from document
|
145
|
+
def extract_data(doc)
|
146
|
+
res = []
|
147
|
+
return res
|
148
|
+
end
|
149
|
+
|
150
|
+
# Extract links to another documents from this document
|
151
|
+
def extract_links(doc)
|
152
|
+
res = []
|
153
|
+
return res
|
154
|
+
end
|
155
|
+
end # CrawlerBase
|
156
|
+
end # Crawler
|
157
|
+
end # Apollo
|