ferrumwizard 0.1.3 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5eb39e3f7b96b87809950695d7987529b5d2abd7fe8a445e5e658453694d3c4
4
- data.tar.gz: ab1de29c5cb1c2c9a904069e0a6ef6d8bf8cbdadd68eba2ad1992945c4189948
3
+ metadata.gz: 2b8068fb08b6eb2a2c96e42a22e4e2290484fa98ca27271b3731d858e39d7bf5
4
+ data.tar.gz: a939996048a46f321bf66c4dc86991f9a37c91393599782e9d3a147a473e2216
5
5
  SHA512:
6
- metadata.gz: 511884c66a570b8fe74ab14109f3f731316a74824869b73a46cf26bff6b7fbc4f63a92848790229a95aa753b91ff3ada227ebdfa6ed19fe40641da812597d394
7
- data.tar.gz: 166d533864edff9c4b3cd00e9174064a900532bf8e420edf7ea1c4d16dad3145bb99acc0ab93a4ababf7bb76a12f5b383018a1f61ead98433cec2b1de07814d0
6
+ metadata.gz: 17c6c8faf26098abcbd716cedb7ba4a4c9d935595af6c4e1626887b3a465ab6632d68502f4f01f5f61f5af59a78c0a629da36e4ea6c0b11a35d1ce1f994872d1
7
+ data.tar.gz: 950098796922e1b514df83e92438f533ad92fa6bfb40208001d3b1de12b7810b87fa3f888a8f6733061e5f0672fb8e2919358ca52a11bef12b5bb24b40bcf451
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/ferrumwizard.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  # file: ferrumwizard.rb
4
4
 
5
+ require 'yaml'
5
6
  require 'rexle'
6
7
  require 'ferrum'
7
8
 
@@ -10,125 +11,204 @@ class FerrumWizard
10
11
 
11
12
  attr_reader :browser, :links, :radio, :buttons, :js_methods
12
13
 
13
- def initialize(url, headless: true, timeout: 10, debug: false)
14
+ def initialize(url=nil, headless: true, timeout: 10, cookies: nil,
15
+ debug: false)
14
16
 
15
17
  @url, @debug = url, debug
16
18
  @browser = Ferrum::Browser.new headless: headless, timeout: timeout
17
- sleep 2
18
-
19
- @browser.goto url if url
19
+ sleep 3
20
+
21
+ if url then
22
+
23
+ loadx(cookies) if cookies
24
+
25
+ @browser.goto(@url)
26
+ @browser.network.wait_for_idle
27
+ sleep 4
28
+
29
+ end
20
30
  end
21
-
31
+
22
32
  def inspect()
23
33
  "#<FerrumWizard>"
24
34
  end
25
-
35
+
36
+ # Intended to load all the cookies for a user to login automatically
37
+ #
38
+ # Follow these steps to load the cookies file:
39
+ #
40
+ # 1. launch the Ferrum browser
41
+ # fw = FerrumWizard.new( headless: false, debug: false)
42
+ #
43
+ # 2. load the cookies before you visit the website
44
+ # fw.load_cookies('/tmp/indeed2.txt')
45
+ #
46
+ # 3. visit the website
47
+ # url='https://somewebsite.com'
48
+ # fw.browser.goto(url)
49
+ #
50
+ def load_cookies(filepath)
51
+
52
+ rawcookies = YAML.load(File.read(filepath))
53
+
54
+ rawcookies.each do |h|
55
+
56
+ if @debug then
57
+ puts 'name: ' + h['name']
58
+ puts 'h: ' + h.inspect
59
+ sleep 0.7
60
+ end
61
+
62
+ browser.cookies.set(name: h['name'], value: h['value'],
63
+ domain: h['domain'], expires: h['expires'],
64
+ httponly: h['httpOnly'])
65
+ end
66
+
67
+ end
68
+
69
+ alias loadx load_cookies
70
+
26
71
  def login(usernamex=nil, passwordx=nil, username: usernamex, password: passwordx)
27
-
28
- puts 'username: ' + username.inspect if @debug
29
72
 
30
- b = @browser
31
- b.goto(@url)
32
- @browser.network.wait_for_idle
33
- sleep 3
73
+ puts 'username: ' + username.inspect if @debug
34
74
 
35
75
  # search for the username input box
36
- e_username = b.at_xpath('//input[@type="email"]')
76
+ e_username = @browser.at_xpath('//input[@type="email"]')
37
77
  puts 'e_username: ' + e_username.inspect if @debug
38
78
  sleep 1
39
79
  # search for the password input box
40
- e_password = b.at_xpath('//input[@type="password"]')
80
+ found = @browser.at_xpath('//input[@type="password"]')
81
+
82
+ e_password = if found then
83
+ found
84
+ else
85
+ @browser.xpath('//input').find {|x| x.property(:id) =~ /password/i}
86
+ end
87
+
41
88
  sleep 1
42
-
89
+
43
90
  if username and e_username then
44
91
  puts 'entering the username' if @debug
45
- e_username.focus.type(username)
92
+ e_username.focus.type(username)
46
93
  sleep 1
47
94
  end
48
-
95
+
49
96
  e_password.focus.type(password, :Enter) if e_password
50
-
97
+
51
98
  after_login()
52
-
53
- end
54
-
99
+
100
+ end
101
+
102
+ # login2 is used for websites where the user is presented with the username
103
+ # input box on the first page and the password input box on the next page.
104
+ #
55
105
  def login2(usernamex=nil, passwordx=nil, username: usernamex, password: passwordx)
56
-
57
- puts 'username: ' + username.inspect if @debug
58
106
 
59
- b = @browser
60
- b.goto(@url)
61
- @browser.network.wait_for_idle
62
- sleep 3
107
+ puts 'username: ' + username.inspect if @debug
63
108
 
64
109
  # search for the username input box
65
- e_username = b.at_xpath('//input[@type="email"]')
110
+ e_username = @browser.at_xpath('//input[@type="email"]')
66
111
  puts 'e_username: ' + e_username.inspect if @debug
67
112
  sleep 1
68
113
  # search for the password input box
69
-
114
+
70
115
  if username and e_username then
71
116
  puts 'entering the username' if @debug
72
- e_username.focus.type(username, :Enter)
117
+ e_username.focus.type(username, :Enter)
73
118
  sleep 2
74
119
  end
75
120
 
76
- e_password = b.at_xpath('//input[@type="password"]')
121
+ e_password = @browser.at_xpath('//input[@type="password"]')
77
122
  sleep 1
78
-
123
+
79
124
  e_password.focus.type(password, :Enter) if e_password
80
-
125
+
81
126
  after_login()
82
127
 
83
-
84
- end
85
-
128
+
129
+ end
130
+
86
131
  def quit
87
132
  @browser.quit
88
133
  end
89
-
134
+
90
135
  def scan_page()
91
-
92
- @doc = Rexle.new @browser.body
136
+
137
+ @doc = Rexle.new @browser.body
93
138
  fetch_links()
94
- scan_form_elements()
139
+ scan_form_elements()
95
140
  scan_js_links()
96
- self
141
+ @browser.mouse.scroll_to(0, 800)
142
+ self
143
+ end
144
+
145
+ # Saves all cookies for a given website into a YAML file
146
+ # see also load_cookies()
147
+ #
148
+ # To use this method follow these steps:
149
+ #
150
+ # 1. launch the web browser through Ferrum
151
+ # fw = FerrumWizard.new(url, headless: false, debug: false)
152
+ #
153
+ # 2. go to the browser and login using your credentials
154
+ # fw.save_cookies(filepath)
155
+ #
156
+ # 3. exit the IRB session
157
+ #
158
+ def save_cookies(filepath=Tempfile.new('ferrum').path)
159
+
160
+ rawcookies = @browser.cookies.all.keys.map do |key|
161
+
162
+ if @debug then
163
+ puts 'key: ' + key.inspect
164
+ sleep 0.5
165
+ end
166
+
167
+ s = @browser.cookies[key].inspect
168
+ a = s.scan(/"([^"]+)"=\>/)
169
+ s2 = s[/(?<=@attributes=).*(?=>)/]
170
+ eval(s2)
171
+
172
+ end
173
+
174
+ File.write filepath, rawcookies.to_yaml
175
+
97
176
  end
98
-
177
+
99
178
  def submit(h)
100
179
 
101
180
  e = nil
102
-
181
+
103
182
  h.each do |key, value|
104
- e = @browser.xpath('//input').find {|x| x.attribute('name') == key.to_s}
105
- e.focus.type(value)
183
+ e = @browser.xpath('//input').find {|x| x.attribute('name') == key.to_s}
184
+ e.focus.type(value)
106
185
  end
107
-
108
- e.focus.type('', :Enter)
109
-
110
- sleep 4
186
+
187
+ e.focus.type('', :Enter)
188
+
189
+ sleep 4
111
190
  scan_page()
112
-
191
+
113
192
  end
114
193
 
115
194
  def to_rb()
116
195
  end
117
-
196
+
118
197
  private
119
-
198
+
120
199
  def after_login()
121
-
122
- @browser.network.wait_for_idle
200
+
201
+ @browser.network.wait_for_idle
123
202
  sleep 4
124
203
  scan_page()
125
-
204
+
126
205
  @browser.base_url = File.dirname(@browser.url)
127
206
  @browser.mouse.scroll_to(0, 800)
128
207
  self
129
-
208
+
130
209
  end
131
-
210
+
211
+
132
212
  def fetch_buttons()
133
213
 
134
214
  a2 = @browser.xpath('//input[@type="button"]')
@@ -144,24 +224,24 @@ class FerrumWizard
144
224
  buttons = @buttons
145
225
 
146
226
  names.each do |name|
147
-
227
+
148
228
  define_singleton_method name.to_sym do
149
229
  buttons[name].click
150
230
  @browser.network.wait_for_idle
151
231
  sleep = 1
152
232
  self
153
233
  end
154
-
234
+
155
235
  end
156
236
 
157
237
  end
158
-
238
+
159
239
  def fetch_links()
160
-
240
+
161
241
  all_links = @doc.root.xpath('//a[@href]')
162
-
242
+
163
243
  all_links.each do |x|
164
-
244
+
165
245
  if x.plaintext.empty? then
166
246
  x.text = x.attributes[:href].sub(/\.\w+$/,'')[/([^\/]+)$/].split(/[_]|(?=[A-Z])/).join(' ')
167
247
  else
@@ -169,76 +249,74 @@ class FerrumWizard
169
249
  end
170
250
 
171
251
  end
172
-
252
+
173
253
  valid_links = all_links.reject do |x|
174
-
254
+
175
255
  puts 'x: ' + x.inspect if @debug
176
256
  r = (x.attributes[:target] == '_blank')
177
257
 
178
258
  puts 'r: ' + r.inspect if @debug
179
259
  r
180
-
260
+
181
261
  end
182
-
262
+
183
263
  indices = valid_links.map {|x| all_links.index x}
184
264
 
185
265
  active_links = @browser.xpath('//a[@href]')
186
266
  valid_active_links = indices.map {|n| active_links[n]}
187
-
188
267
 
189
- @links = valid_active_links.flat_map.with_index do |x, i|
268
+
269
+ @links = valid_active_links.flat_map.with_index do |x, i|
190
270
 
191
271
  a = valid_links[i].text.split(/\W+/).map {|label| [label, x]}
192
272
  a << [valid_links[i].text, x]
193
-
273
+
194
274
  puts 'a: ' + a.inspect if @debug
195
275
  a + a.map {|x2, obj| [x2.downcase, obj]}
196
-
276
+
197
277
  end.to_h
198
-
278
+
199
279
  names = @links.keys.map(&:downcase).uniq.select {|x| x =~ /^[\w ]+$/}
200
280
  links = @links
201
-
281
+
202
282
  names.each do |name|
203
-
283
+
204
284
  define_singleton_method name.gsub(/ +/,'_').to_sym do
205
-
285
+
206
286
  links[name].click
207
287
  @browser.network.wait_for_idle
208
-
288
+
209
289
  sleep 1
210
290
  scan_page()
211
291
  self
212
-
292
+
213
293
  end
214
-
294
+
215
295
  end
216
-
296
+
217
297
  end
218
298
 
219
299
  def scan_form_elements()
220
-
300
+
221
301
  # find radio buttons
222
-
223
- #a = doc.root.xpath('//input[@type="radio"]')
302
+
224
303
  a = @browser.xpath('//input[@type="radio"]')
225
- #h = a.group_by {|x| x.attributes[:name]}
226
304
  h = a.group_by {|x| x.attribute('name')}
227
305
  @radio = h.values
228
306
  define_singleton_method(:on) { @radio[0][0].click; self }
229
307
  define_singleton_method(:off) { @radio[0][1].click; self }
230
-
308
+
231
309
  fetch_buttons()
232
-
310
+
233
311
  end
234
-
312
+
235
313
  def scan_js_links()
236
-
314
+
237
315
  @js_methods = {}
238
316
  b = @browser
239
-
317
+
240
318
  b.xpath('//a').select {|x| x.attribute('href') =~ /^javascript/}.each do |e|
241
-
319
+
242
320
 
243
321
  s = e.attribute('href')[/(?<=^javascript:)[^\(]+/]
244
322
  puts 's: ' + s.inspect if @debug
@@ -248,17 +326,17 @@ class FerrumWizard
248
326
  a << [s.split(/\W+|(?=[A-Z])/).join('_').downcase, s]
249
327
  #@js_methods[s] = a
250
328
 
251
- a.concat a.map {|x, name| [x.downcase, name] }
329
+ a.concat a.map {|x, name| [x.downcase, name] }
252
330
 
253
331
  puts 'a: ' + a.inspect if @debug
254
332
 
255
333
  a.uniq.select {|x, _| x =~ /^[a-z0-9_]+$/}.each do |x, name|
256
-
334
+
257
335
  if @debug then
258
336
  puts 'x: ' + x.inspect
259
337
  puts 'name: ' + name.inspect
260
338
  end
261
-
339
+
262
340
  define_singleton_method(x.to_sym) do |*args|
263
341
  #args = raw_args.map {|x| x[/^[0-9]+$/] ? x.to_i : x}
264
342
  js_method = "%s(%s)" % [name, args.map(&:inspect).join(', ')]
@@ -267,9 +345,9 @@ class FerrumWizard
267
345
  sleep 4
268
346
  self.scan_page()
269
347
  end
270
-
348
+
271
349
  end
272
-
350
+
273
351
  end
274
352
  end
275
353
 
@@ -278,7 +356,7 @@ class FerrumWizard
278
356
  puts 'method_missing: ' + method_name.inspect if @debug
279
357
  node = @browser.at_css '.' + method_name.to_s
280
358
  node.text if node
281
-
282
- end
283
-
359
+
360
+ end
361
+
284
362
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ferrumwizard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Robertson
@@ -11,31 +11,31 @@ cert_chain:
11
11
  - |
12
12
  -----BEGIN CERTIFICATE-----
13
13
  MIIEXjCCAsagAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDDCFnZW1t
14
- YXN0ZXIvREM9amFtZXNyb2JlcnRzb24vREM9ZXUwHhcNMjAwNzMwMTYxOTE5WhcN
15
- MjEwNzMwMTYxOTE5WjAsMSowKAYDVQQDDCFnZW1tYXN0ZXIvREM9amFtZXNyb2Jl
16
- cnRzb24vREM9ZXUwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDhNEIG
17
- /Ab4nneih/AQFMcYk76JCiy26Xcy5uxd9ib7Emkj/9sZo6nxuSBaH03+Ixv3jgJs
18
- TxZyaIKRsESFFmupYmKsyatCGGaBEsDb210ZBm313rP2Pk2fGrUtON0CjwJljWxR
19
- 8pHuglEXrGN/XhVicy7sZLJ2nVnvRtyiKi92XmY0S9LaCkWlOx2f3D7yiazkmHh5
20
- 59nHiGNlZ/SOzFrRMdBvkWZYHgqUEBv0KxEuMqW65U4HdlQImcwqu8XOWH9kutof
21
- yyisv03kPqMvrOC8ptG/TieKYK0JuY23gS9MrVxkrf0gX3IQLY21JWG9t9uRImc/
22
- kHC+EJ2rI8HQqcq/v6dndJb6MhYEhj7R5XsZqlfsLFo21FFBAyaPrqPRUstnW5U0
23
- /tCpcuFyZJeRPqQ8LSlRGDuB/TdmV9dF+P5aGS32k9Okf9L6E6x3OGV29eMHSdDt
24
- LOOB8l0EJbNXzpvYW+htziU8TbuzRQU8K7uTeAfpMUg4auPxdVyQpJcQWXcCAwEA
25
- AaOBijCBhzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU2+nN7PCw
26
- js3NFmK8b17Ji/t+dvwwJgYDVR0RBB8wHYEbZ2VtbWFzdGVyQGphbWVzcm9iZXJ0
14
+ YXN0ZXIvREM9amFtZXNyb2JlcnRzb24vREM9ZXUwHhcNMjIwMzMwMTI1ODU1WhcN
15
+ MjMwMzMwMTI1ODU1WjAsMSowKAYDVQQDDCFnZW1tYXN0ZXIvREM9amFtZXNyb2Jl
16
+ cnRzb24vREM9ZXUwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDROL32
17
+ 3LQKGcDR6x6XFa1US/Vq98DVnMeZHCSKdf471I4gJIOA7sQnrQTB6IZKTxb94Wjr
18
+ OSeGzlJpVq6pa7ltxvb9T7YQVVRrXYMC+u0gD9ukolnkpV/4Rh2/IIMxSNKncoZB
19
+ LKPseizGKlli4gs134gAu3wuWdCC7/UWPG/XyocdJC8tLtf/zi4JuRJTojKqYLOp
20
+ KsP9jHPmGVr81cW8HePmhQ/+LiYlKDE4Fwj4yl16XqhF7/5YOz9e5LOHsMUEord4
21
+ JscQ3GnhMfEXGJpwqCwNEpM3xAwcHp2DDdrwtT36ujSfnTJ3UpUIQUKVehA2i9rm
22
+ uDcDTr1PATGcOMPpExvLZu3a9uC81mj9z+axH5mWQ7jZ92sze79oAQTsMiMyBavJ
23
+ djSpnVBo71PFk8QekgIVVBIzG0iN5zoNUrSthvL/xUWXM6ea015HEDCCIEL417ID
24
+ humVWZyzKf7ITCdZWcxTgTgFfuPMctcICT5u7va+FrycYpdtt8kXvtD3VnkCAwEA
25
+ AaOBijCBhzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUDipvmNU2
26
+ WydgAK8QPGb0vhhoGl0wJgYDVR0RBB8wHYEbZ2VtbWFzdGVyQGphbWVzcm9iZXJ0
27
27
  c29uLmV1MCYGA1UdEgQfMB2BG2dlbW1hc3RlckBqYW1lc3JvYmVydHNvbi5ldTAN
28
- BgkqhkiG9w0BAQsFAAOCAYEAlaYpTQ2vLuKU/nJl1inw9iE9XCwnTmIhmA9lnu1q
29
- QKKCd7Z2PwtkahbDvMVQ347DQZQAanuZmtTPFMc4FDA530qJtwoYk03FTQXBh12M
30
- d4C27VP9BOrUQcxkqtnTo+4Z60taszXqyPsPYU+Fd8AZUPeS5TOYG52OXTQ+q+pO
31
- vNxkRP9oEka81ZrN1y3r3YaFHATZzf4pJo0HupZvMsQwa33/vA+xxxpDeTuWytNN
32
- O0mYbo8Em2LnPnE8ehOnniDGXIIaDO9B1Qbbr0GhNCIWq3JIcbI2IBCKFWA6HyNF
33
- yCdr7ZqPrnxXlhhnTPLFkzR/0+XxpbrdW4zb6uQqX92/tiUqP9uKf5dBEVoCWax/
34
- IWPJE5JXx2iMvE9cWe4bFCUi7cZT7HsL6jkdUWxeTvsfc7XMbE8eWtHHiG6NjeFJ
35
- 7e24hNRMt3t/JE9ogEO4JzFUH2vq2zzR5X9JQqEclWfwHi4cf8bZFJ7spjZQPjSZ
36
- Ok3rs0A+kW4ixAj1rDYuoyG/
28
+ BgkqhkiG9w0BAQsFAAOCAYEAHqRvm6iqjJ+bpEzSSgVmOMOMcgIoN6px1LMVAOmY
29
+ BJpF5F0fJr99thc1EYZJoRTwEcXJYhCTqKg+3xhNKpCzk2qHsaLKYEygPeBpyJOg
30
+ LyfHLrj98QLPYyFzqhWsqZAAAGC9WSF/kBJazpuotU2ec/Xw/e3NPopedV/Zvuhs
31
+ +/OKZWwRez/hg97ckaCYAp/7OdrVhJvR87MaQnN52Uk8OQbuPSyUNQUJ044HWHtu
32
+ lEJjsDetEFhNB69j3wAIMjMEZao29/dZhALbUDp+9ewK7uYbrX9Bo68NX+H+XcCZ
33
+ VFrdrjkyJUOHwSmvjYXN1V0Yz8kVVFU7E+Q4RHL8yAwBv+ynd927HtZVjs+455Pc
34
+ z+9gNpBQVr6LLXLJgJF2pTaIoYhgG6pcoMQHGVoxWdKzvOcl0h1epeJSp/aynX/r
35
+ FK+cyrQNA9DLJYJuz6uO7Z+gXZWjwAO38LUwF01w49asSv/5ZH2HH/EauX5xWpe+
36
+ ry6lYQlb8j50Iys5elAy1p0i
37
37
  -----END CERTIFICATE-----
38
- date: 2020-08-01 00:00:00.000000000 Z
38
+ date: 2022-03-30 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rexle
@@ -46,7 +46,7 @@ dependencies:
46
46
  version: '1.5'
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 1.5.7
49
+ version: 1.5.14
50
50
  type: :runtime
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
@@ -56,29 +56,29 @@ dependencies:
56
56
  version: '1.5'
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 1.5.7
59
+ version: 1.5.14
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: ferrum
62
62
  requirement: !ruby/object:Gem::Requirement
63
63
  requirements:
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: 0.9.0
67
64
  - - "~>"
68
65
  - !ruby/object:Gem::Version
69
- version: '0.9'
66
+ version: '0.11'
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0.11'
70
70
  type: :runtime
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: 0.9.0
77
74
  - - "~>"
78
75
  - !ruby/object:Gem::Version
79
- version: '0.9'
76
+ version: '0.11'
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0.11'
80
80
  description:
81
- email: james@jamesrobertson.eu
81
+ email: digital.robertson@gmail.com
82
82
  executables: []
83
83
  extensions: []
84
84
  extra_rdoc_files: []
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
105
  requirements: []
106
- rubygems_version: 3.0.3
106
+ rubygems_version: 3.2.22
107
107
  signing_key:
108
108
  specification_version: 4
109
109
  summary: Makes web scraping easier using the Ferrum gem.
metadata.gz.sig CHANGED
Binary file