nokolexbor 0.7.0-aarch64-linux
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 +7 -0
- data/lib/nokolexbor/3.1/nokolexbor.so +0 -0
- data/lib/nokolexbor/3.2/nokolexbor.so +0 -0
- data/lib/nokolexbor/3.3/nokolexbor.so +0 -0
- data/lib/nokolexbor/3.4/nokolexbor.so +0 -0
- data/lib/nokolexbor/4.0/nokolexbor.so +0 -0
- data/lib/nokolexbor/builder.rb +223 -0
- data/lib/nokolexbor/document.rb +168 -0
- data/lib/nokolexbor/document_fragment.rb +42 -0
- data/lib/nokolexbor/node.rb +775 -0
- data/lib/nokolexbor/node_set.rb +293 -0
- data/lib/nokolexbor/version.rb +5 -0
- data/lib/nokolexbor/xpath.rb +69 -0
- data/lib/nokolexbor/xpath_context.rb +14 -0
- data/lib/nokolexbor.rb +50 -0
- metadata +85 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nokolexbor
|
|
4
|
+
class NodeSet < Nokolexbor::Node
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
# Create a NodeSet with +document+ defaulting to +list+.
|
|
8
|
+
#
|
|
9
|
+
# @yield [Document]
|
|
10
|
+
#
|
|
11
|
+
# @return [Document]
|
|
12
|
+
def self.new(document, list = [])
|
|
13
|
+
obj = allocate
|
|
14
|
+
if document.is_a?(Document) || document.nil?
|
|
15
|
+
obj.instance_variable_set(:@document, document)
|
|
16
|
+
elsif document.is_a?(Node)
|
|
17
|
+
obj.instance_variable_set(:@document, document.document)
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "Expected a Document or Node, got #{document.class}"
|
|
20
|
+
end
|
|
21
|
+
list.each { |x| obj << x }
|
|
22
|
+
yield obj if block_given?
|
|
23
|
+
obj
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Iterate over each node.
|
|
27
|
+
#
|
|
28
|
+
# @yield [Node]
|
|
29
|
+
def each
|
|
30
|
+
return to_enum unless block_given?
|
|
31
|
+
|
|
32
|
+
0.upto(length - 1) do |x|
|
|
33
|
+
yield self[x]
|
|
34
|
+
end
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get the first +n+ elements of the NodeSet.
|
|
39
|
+
#
|
|
40
|
+
# @param n [Numeric,nil]
|
|
41
|
+
#
|
|
42
|
+
# @return [Node,Array<Node>] {Node} if +n+ is nil, otherwise {Array<Node>}
|
|
43
|
+
def first(n = nil)
|
|
44
|
+
return self[0] unless n
|
|
45
|
+
|
|
46
|
+
list = []
|
|
47
|
+
[n, length].min.times { |i| list << self[i] }
|
|
48
|
+
list
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get the last element of the NodeSet.
|
|
52
|
+
#
|
|
53
|
+
# @return [Node,nil]
|
|
54
|
+
def last
|
|
55
|
+
self[-1]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Boolean] true if this NodeSet is empty.
|
|
59
|
+
def empty?
|
|
60
|
+
length == 0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Insert +node+ before the first Node in this NodeSet
|
|
64
|
+
def before(node)
|
|
65
|
+
first.before(node)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Insert +node+ after the last Node in this NodeSet
|
|
69
|
+
def after(node)
|
|
70
|
+
last.after(node)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @return [Integer] The index of the first node in this NodeSet that is equal to +node+ or meets the given block. Returns nil if no match is found.
|
|
74
|
+
def index(node = nil)
|
|
75
|
+
if node
|
|
76
|
+
each_with_index { |member, j| return j if member == node }
|
|
77
|
+
elsif block_given?
|
|
78
|
+
each_with_index { |member, j| return j if yield(member) }
|
|
79
|
+
end
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get the content of all contained Nodes.
|
|
84
|
+
#
|
|
85
|
+
# @return [String]
|
|
86
|
+
def content
|
|
87
|
+
self.map(&:content).join
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
alias_method :text, :content
|
|
91
|
+
alias_method :inner_text, :content
|
|
92
|
+
alias_method :to_str, :content
|
|
93
|
+
|
|
94
|
+
# Get the inner html of all contained Nodes.
|
|
95
|
+
#
|
|
96
|
+
# @return [String]
|
|
97
|
+
def inner_html(*args)
|
|
98
|
+
self.map { |n| n.inner_html(*args) }.join
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Convert this NodeSet to HTML.
|
|
102
|
+
#
|
|
103
|
+
# @return [String]
|
|
104
|
+
def outer_html(*args)
|
|
105
|
+
self.map { |n| n.outer_html(*args) }.join
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
alias_method :to_s, :outer_html
|
|
109
|
+
alias_method :to_html, :outer_html
|
|
110
|
+
alias_method :serialize, :outer_html
|
|
111
|
+
|
|
112
|
+
# Remove all nodes in this NodeSet.
|
|
113
|
+
#
|
|
114
|
+
# @see Node#remove
|
|
115
|
+
def remove
|
|
116
|
+
self.each(&:remove)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
alias_method :unlink, :remove
|
|
120
|
+
alias_method :to_ary, :to_a
|
|
121
|
+
|
|
122
|
+
# Destroy all nodes in the NodeSet.
|
|
123
|
+
#
|
|
124
|
+
# @see Node#destroy
|
|
125
|
+
def destroy
|
|
126
|
+
self.each(&:destroy)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @return [Node,nil] The last element of this NodeSet and removes it. Returns
|
|
130
|
+
# +nil+ if the set is empty.
|
|
131
|
+
def pop
|
|
132
|
+
return nil if length == 0
|
|
133
|
+
|
|
134
|
+
delete(last)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @return [Node,nil] The first element of this NodeSet and removes it. Returns
|
|
138
|
+
# +nil+ if the set is empty.
|
|
139
|
+
def shift
|
|
140
|
+
return nil if length == 0
|
|
141
|
+
|
|
142
|
+
delete(first)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# @return [Boolean] true if two NodeSets contain the same number
|
|
146
|
+
# of elements and each element is equal to the corresponding
|
|
147
|
+
# element in the other NodeSet.
|
|
148
|
+
def ==(other)
|
|
149
|
+
return false unless other.is_a?(NodeSet)
|
|
150
|
+
return false unless length == other.length
|
|
151
|
+
|
|
152
|
+
each_with_index do |node, i|
|
|
153
|
+
return false unless node == other[i]
|
|
154
|
+
end
|
|
155
|
+
true
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @return [NodeSet] A new NodeSet containing all the children of all the nodes in
|
|
159
|
+
# the NodeSet.
|
|
160
|
+
def children
|
|
161
|
+
node_set = NodeSet.new(@document)
|
|
162
|
+
each do |node|
|
|
163
|
+
node.children.each { |n| node_set.push(n) }
|
|
164
|
+
end
|
|
165
|
+
node_set
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# @return [NodeSet] A new NodeSet containing all the nodes in the NodeSet
|
|
169
|
+
# in reverse order.
|
|
170
|
+
def reverse
|
|
171
|
+
node_set = NodeSet.new(@document)
|
|
172
|
+
(length - 1).downto(0) do |x|
|
|
173
|
+
node_set.push(self[x])
|
|
174
|
+
end
|
|
175
|
+
node_set
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Wrap all nodes of this NodeSet with +node_or_tags+.
|
|
179
|
+
#
|
|
180
|
+
# @see Node#wrap
|
|
181
|
+
#
|
|
182
|
+
# @return [NodeSet] +self+, to support chaining.
|
|
183
|
+
def wrap(node_or_tags)
|
|
184
|
+
map { |node| node.wrap(node_or_tags) }
|
|
185
|
+
self
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Add the class attribute +name+ to all containing nodes.
|
|
189
|
+
#
|
|
190
|
+
# @see Node#add_class
|
|
191
|
+
def add_class(name)
|
|
192
|
+
each do |el|
|
|
193
|
+
el.add_class(name)
|
|
194
|
+
end
|
|
195
|
+
self
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Append the class attribute +name+ to all containing nodes.
|
|
199
|
+
#
|
|
200
|
+
# @see Node#append_class
|
|
201
|
+
def append_class(name)
|
|
202
|
+
each do |el|
|
|
203
|
+
el.append_class(name)
|
|
204
|
+
end
|
|
205
|
+
self
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Remove the class attribute +name+ from all containing nodes.
|
|
209
|
+
#
|
|
210
|
+
# @see Node#remove_class
|
|
211
|
+
def remove_class(name = nil)
|
|
212
|
+
each do |el|
|
|
213
|
+
el.remove_class(name)
|
|
214
|
+
end
|
|
215
|
+
self
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Remove the attributed named +name+ from all containing nodes.
|
|
219
|
+
#
|
|
220
|
+
# @see Node#remove_attr
|
|
221
|
+
def remove_attr(name)
|
|
222
|
+
each { |el| el.delete(name) }
|
|
223
|
+
self
|
|
224
|
+
end
|
|
225
|
+
alias_method :remove_attribute, :remove_attr
|
|
226
|
+
|
|
227
|
+
# Set attributes on each Node in the NodeSet, or get an
|
|
228
|
+
# attribute from the first Node in the NodeSet.
|
|
229
|
+
#
|
|
230
|
+
# @example Get an attribute from the first Node in a NodeSet.
|
|
231
|
+
# node_set.attr("href")
|
|
232
|
+
#
|
|
233
|
+
# @example Set attributes on each node.
|
|
234
|
+
# node_set.attr("href" => "http://example.com", "class" => "a")
|
|
235
|
+
# node_set.attr("href", "http://example.com")
|
|
236
|
+
# node_set.attr("href") { |node| "http://example.com" }
|
|
237
|
+
#
|
|
238
|
+
# @return [NodeSet] +self+, to support chaining of calls.
|
|
239
|
+
def attr(key, value = nil, &block)
|
|
240
|
+
unless key.is_a?(Hash) || (key && (value || block))
|
|
241
|
+
return first&.attribute(key)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
hash = key.is_a?(Hash) ? key : { key => value }
|
|
245
|
+
|
|
246
|
+
hash.each do |k, v|
|
|
247
|
+
each do |node|
|
|
248
|
+
node[k] = v || yield(node)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
self
|
|
253
|
+
end
|
|
254
|
+
alias_method :set, :attr
|
|
255
|
+
alias_method :attribute, :attr
|
|
256
|
+
|
|
257
|
+
# (see Node#xpath)
|
|
258
|
+
def xpath(*args)
|
|
259
|
+
paths, handler, ns, binds = extract_params(args)
|
|
260
|
+
|
|
261
|
+
NodeSet.new(@document) do |set|
|
|
262
|
+
each do |node|
|
|
263
|
+
node.send(:xpath_internal, node, paths, handler, ns, binds).each do |inner_node|
|
|
264
|
+
set << inner_node
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# (see Node#nokogiri_css)
|
|
271
|
+
def nokogiri_css(*args)
|
|
272
|
+
rules, handler, ns, _ = extract_params(args)
|
|
273
|
+
paths = css_rules_to_xpath(rules, ns)
|
|
274
|
+
|
|
275
|
+
NodeSet.new(@document) do |set|
|
|
276
|
+
each do |node|
|
|
277
|
+
node.send(:xpath_internal, node, paths, handler, ns, nil).each do |inner_node|
|
|
278
|
+
set << inner_node
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def inspect
|
|
285
|
+
"[#{map(&:inspect).join(', ')}]"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
private
|
|
289
|
+
|
|
290
|
+
IMPLIED_XPATH_CONTEXTS = [".//", "self::"].freeze # :nodoc:
|
|
291
|
+
|
|
292
|
+
end
|
|
293
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nokolexbor
|
|
4
|
+
module XPath
|
|
5
|
+
class SyntaxError < StandardError
|
|
6
|
+
attr_reader :domain
|
|
7
|
+
attr_reader :code
|
|
8
|
+
attr_reader :level
|
|
9
|
+
attr_reader :file
|
|
10
|
+
attr_reader :line
|
|
11
|
+
attr_reader :str1
|
|
12
|
+
attr_reader :str2
|
|
13
|
+
attr_reader :str3
|
|
14
|
+
attr_reader :int1
|
|
15
|
+
attr_reader :column
|
|
16
|
+
|
|
17
|
+
###
|
|
18
|
+
# return true if this is a non error
|
|
19
|
+
def none?
|
|
20
|
+
level == 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
###
|
|
24
|
+
# return true if this is a warning
|
|
25
|
+
def warning?
|
|
26
|
+
level == 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
###
|
|
30
|
+
# return true if this is an error
|
|
31
|
+
def error?
|
|
32
|
+
level == 2
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
###
|
|
36
|
+
# return true if this error is fatal
|
|
37
|
+
def fatal?
|
|
38
|
+
level == 3
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_s
|
|
42
|
+
message = super.chomp
|
|
43
|
+
[location_to_s, level_to_s, message]
|
|
44
|
+
.compact.join(": ")
|
|
45
|
+
.force_encoding(message.encoding)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def level_to_s
|
|
51
|
+
case level
|
|
52
|
+
when 3 then "FATAL"
|
|
53
|
+
when 2 then "ERROR"
|
|
54
|
+
when 1 then "WARNING"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def nil_or_zero?(attribute)
|
|
59
|
+
attribute.nil? || attribute.zero?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def location_to_s
|
|
63
|
+
return nil if nil_or_zero?(line) && nil_or_zero?(column)
|
|
64
|
+
|
|
65
|
+
"#{line}:#{column}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nokolexbor
|
|
4
|
+
class XPathContext
|
|
5
|
+
###
|
|
6
|
+
# Register namespaces in +namespaces+
|
|
7
|
+
def register_namespaces(namespaces)
|
|
8
|
+
namespaces.each do |k, v|
|
|
9
|
+
k = k.to_s.gsub(/.*:/, "") # strip off 'xmlns:' or 'xml:'
|
|
10
|
+
register_ns(k, v)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/nokolexbor.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
# pre-compiled extension by rake-compiler is located inside lib/nokolexbor/<ruby_version>/
|
|
5
|
+
RUBY_VERSION =~ /(\d+\.\d+)/
|
|
6
|
+
require "nokolexbor/#{Regexp.last_match(1)}/nokolexbor"
|
|
7
|
+
rescue LoadError => e
|
|
8
|
+
if /GLIBC/.match?(e.message)
|
|
9
|
+
warn(<<~EOM)
|
|
10
|
+
ERROR: It looks like you're trying to use Nokolexbor as a precompiled native gem on a system
|
|
11
|
+
with an unsupported version of glibc.
|
|
12
|
+
#{e.message}
|
|
13
|
+
If that's the case, then please install Nokolexbor via the `ruby` platform gem:
|
|
14
|
+
gem install nokolexbor --platform=ruby
|
|
15
|
+
or:
|
|
16
|
+
bundle config set force_ruby_platform true
|
|
17
|
+
EOM
|
|
18
|
+
raise e
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'nokolexbor/nokolexbor'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require 'nokolexbor/version'
|
|
25
|
+
require 'nokolexbor/node'
|
|
26
|
+
require 'nokolexbor/document'
|
|
27
|
+
require 'nokolexbor/node_set'
|
|
28
|
+
require 'nokolexbor/document_fragment'
|
|
29
|
+
require 'nokolexbor/xpath'
|
|
30
|
+
require 'nokolexbor/xpath_context'
|
|
31
|
+
require 'nokolexbor/builder'
|
|
32
|
+
|
|
33
|
+
module Nokolexbor
|
|
34
|
+
class << self
|
|
35
|
+
def parse(*args)
|
|
36
|
+
Document.parse(*args)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
alias_method :HTML, :parse
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Parse an HTML document, or build one with the DSL when a block is given.
|
|
44
|
+
def Nokolexbor(string_or_io = nil, &block)
|
|
45
|
+
if block
|
|
46
|
+
Nokolexbor::Builder.new(&block).parent
|
|
47
|
+
else
|
|
48
|
+
Nokolexbor.parse(string_or_io)
|
|
49
|
+
end
|
|
50
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nokolexbor
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.7.0
|
|
5
|
+
platform: aarch64-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Yicheng Zhou
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-03-25 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake-compiler
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: minitest
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.0'
|
|
40
|
+
description: Nokolexbor is a high-performance HTML5 parser, with support for both
|
|
41
|
+
CSS selectors and XPath. It's API is designed to be compatible with Nokogiri.
|
|
42
|
+
email: zyc9012@gmail.com
|
|
43
|
+
executables: []
|
|
44
|
+
extensions: []
|
|
45
|
+
extra_rdoc_files: []
|
|
46
|
+
files:
|
|
47
|
+
- lib/nokolexbor.rb
|
|
48
|
+
- lib/nokolexbor/3.1/nokolexbor.so
|
|
49
|
+
- lib/nokolexbor/3.2/nokolexbor.so
|
|
50
|
+
- lib/nokolexbor/3.3/nokolexbor.so
|
|
51
|
+
- lib/nokolexbor/3.4/nokolexbor.so
|
|
52
|
+
- lib/nokolexbor/4.0/nokolexbor.so
|
|
53
|
+
- lib/nokolexbor/builder.rb
|
|
54
|
+
- lib/nokolexbor/document.rb
|
|
55
|
+
- lib/nokolexbor/document_fragment.rb
|
|
56
|
+
- lib/nokolexbor/node.rb
|
|
57
|
+
- lib/nokolexbor/node_set.rb
|
|
58
|
+
- lib/nokolexbor/version.rb
|
|
59
|
+
- lib/nokolexbor/xpath.rb
|
|
60
|
+
- lib/nokolexbor/xpath_context.rb
|
|
61
|
+
homepage: https://github.com/serpapi/nokolexbor
|
|
62
|
+
licenses:
|
|
63
|
+
- MIT
|
|
64
|
+
metadata: {}
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '3.1'
|
|
73
|
+
- - "<"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 4.1.dev
|
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '0'
|
|
81
|
+
requirements: []
|
|
82
|
+
rubygems_version: 4.0.3
|
|
83
|
+
specification_version: 4
|
|
84
|
+
summary: High-performance HTML5 parser, with support for both CSS selectors and XPath.
|
|
85
|
+
test_files: []
|