fetch_util 0.3.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/Rakefile +5 -0
- data/lib/fetch_util/assets/extract.js +1 -1
- data/lib/fetch_util/browser/site_stabilization/gitlab_repo.rb +32 -0
- data/lib/fetch_util/browser/site_stabilization.rb +2 -0
- data/lib/fetch_util/browser/stabilization/page_flow.rb +1 -0
- data/lib/fetch_util/cli.rb +1 -0
- data/lib/fetch_util/fetcher.rb +545 -42
- data/lib/fetch_util/raw_docs_fallback.rb +40 -3
- data/lib/fetch_util/result.rb +100 -3
- data/lib/fetch_util/version.rb +1 -1
- metadata +2 -1
|
@@ -20,6 +20,7 @@ module FetchUtil
|
|
|
20
20
|
"nav",
|
|
21
21
|
"aside",
|
|
22
22
|
"footer",
|
|
23
|
+
".discord",
|
|
23
24
|
".toc",
|
|
24
25
|
".sidebar",
|
|
25
26
|
".breadcrumbs",
|
|
@@ -29,10 +30,18 @@ module FetchUtil
|
|
|
29
30
|
"button"
|
|
30
31
|
].freeze
|
|
31
32
|
DOCS_ROOT_SELECTORS = [
|
|
33
|
+
"main .sl-markdown-content",
|
|
34
|
+
"main [data-pagefind-body]",
|
|
35
|
+
".sl-markdown-content",
|
|
36
|
+
"[data-pagefind-body]",
|
|
32
37
|
"main article",
|
|
33
38
|
"main",
|
|
34
39
|
"article",
|
|
35
40
|
"[role='main']",
|
|
41
|
+
"[class*='content']",
|
|
42
|
+
"[id*='content']",
|
|
43
|
+
"[class*='article']",
|
|
44
|
+
"[id*='article']",
|
|
36
45
|
".content",
|
|
37
46
|
".resource-container"
|
|
38
47
|
].freeze
|
|
@@ -58,7 +67,9 @@ module FetchUtil
|
|
|
58
67
|
return nil unless root
|
|
59
68
|
|
|
60
69
|
prune!(root)
|
|
61
|
-
title =
|
|
70
|
+
title = [fragment_title(document, final_url), first_heading(root), meta_title(document), document.title]
|
|
71
|
+
.map { |candidate| clean_text(candidate) }
|
|
72
|
+
.find { |candidate| !candidate.empty? }
|
|
62
73
|
markdown = markdown_from_root(root, title)
|
|
63
74
|
return nil if clean_text(markdown).length < 40
|
|
64
75
|
|
|
@@ -149,23 +160,49 @@ module FetchUtil
|
|
|
149
160
|
end
|
|
150
161
|
|
|
151
162
|
def docs_root(document)
|
|
163
|
+
candidates = []
|
|
152
164
|
DOCS_ROOT_SELECTORS.each do |selector|
|
|
153
|
-
|
|
154
|
-
|
|
165
|
+
document.css(selector).each do |node|
|
|
166
|
+
text = clean_text(node.text)
|
|
167
|
+
next if text.length < 120
|
|
168
|
+
|
|
169
|
+
candidates << [node, text.length + (node.css("p").length * 200)]
|
|
170
|
+
end
|
|
155
171
|
end
|
|
156
172
|
|
|
173
|
+
best = candidates.max_by { |_node, score| score }
|
|
174
|
+
return best.first.dup if best
|
|
175
|
+
|
|
157
176
|
body = document.at_css("body")
|
|
158
177
|
body&.dup
|
|
159
178
|
end
|
|
160
179
|
|
|
161
180
|
def prune!(root)
|
|
162
181
|
DROP_SELECTORS.each { |selector| root.css(selector).remove }
|
|
182
|
+
prune_leading_promo_cards!(root)
|
|
163
183
|
root.css("*").each do |node|
|
|
164
184
|
text = clean_text(node.text)
|
|
165
185
|
node.remove if text.match?(PRUNED_TEXT_PATTERN)
|
|
166
186
|
end
|
|
167
187
|
end
|
|
168
188
|
|
|
189
|
+
def prune_leading_promo_cards!(root)
|
|
190
|
+
return unless root.at_css("h1, h2")
|
|
191
|
+
|
|
192
|
+
root.css("article.card, .card").each do |node|
|
|
193
|
+
text = clean_text(node.text)
|
|
194
|
+
next if node.ancestors.any? { |ancestor| class_list(ancestor).any? { |klass| %w[landing-card card--fullwidth].include?(klass) } }
|
|
195
|
+
next if node.at_css(".title, .body")
|
|
196
|
+
next if text.empty? || text.length > 600 || node.at_css("h1, h2, h3, h4, h5, h6")
|
|
197
|
+
|
|
198
|
+
node.remove
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def class_list(node)
|
|
203
|
+
node["class"].to_s.split
|
|
204
|
+
end
|
|
205
|
+
|
|
169
206
|
def meta_title(document)
|
|
170
207
|
meta_value(document, "og:title", attr: "property") || document.title
|
|
171
208
|
end
|
data/lib/fetch_util/result.rb
CHANGED
|
@@ -5,11 +5,106 @@ module FetchUtil
|
|
|
5
5
|
attr_reader :url, :final_url, :title, :byline, :excerpt, :site_name,
|
|
6
6
|
:published_time, :canonical_url, :language, :html, :markdown,
|
|
7
7
|
:metadata, :reader_mode, :content_type, :suspect, :warnings,
|
|
8
|
-
:content_completeness_ratio, :content_format, :paywall_state
|
|
8
|
+
:content_completeness_ratio, :content_format, :paywall_state,
|
|
9
|
+
:error_message
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def from_payload(url:, final_url:, payload:, canonical_url:, content_type:, warnings:, suspect:)
|
|
13
|
+
content_completeness_ratio = payload["contentCompletenessRatio"]&.to_f || 1.0
|
|
14
|
+
content_format = payload["contentFormat"]
|
|
15
|
+
paywall_state = payload["paywallState"]
|
|
16
|
+
metadata = payload_metadata(
|
|
17
|
+
payload,
|
|
18
|
+
canonical_url: canonical_url,
|
|
19
|
+
final_url: final_url,
|
|
20
|
+
content_type: content_type,
|
|
21
|
+
suspect: suspect,
|
|
22
|
+
warnings: warnings,
|
|
23
|
+
content_completeness_ratio: content_completeness_ratio,
|
|
24
|
+
content_format: content_format,
|
|
25
|
+
paywall_state: paywall_state
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
new(
|
|
29
|
+
url: url,
|
|
30
|
+
final_url: final_url,
|
|
31
|
+
title: payload["title"],
|
|
32
|
+
byline: payload["byline"],
|
|
33
|
+
excerpt: payload["excerpt"],
|
|
34
|
+
site_name: payload["siteName"],
|
|
35
|
+
published_time: payload["publishedTime"],
|
|
36
|
+
canonical_url: canonical_url,
|
|
37
|
+
language: payload["language"],
|
|
38
|
+
html: payload["html"],
|
|
39
|
+
markdown: payload["markdown"],
|
|
40
|
+
metadata: metadata,
|
|
41
|
+
reader_mode: payload["readerMode"],
|
|
42
|
+
content_type: content_type,
|
|
43
|
+
suspect: suspect,
|
|
44
|
+
warnings: warnings,
|
|
45
|
+
content_completeness_ratio: content_completeness_ratio,
|
|
46
|
+
content_format: content_format,
|
|
47
|
+
paywall_state: paywall_state
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def error(url:, warning:, message:)
|
|
52
|
+
metadata = {
|
|
53
|
+
content_url: url,
|
|
54
|
+
content_type: "error",
|
|
55
|
+
suspect: true,
|
|
56
|
+
warnings: [warning],
|
|
57
|
+
error_message: message
|
|
58
|
+
}.freeze
|
|
59
|
+
|
|
60
|
+
new(
|
|
61
|
+
url: url,
|
|
62
|
+
final_url: url,
|
|
63
|
+
title: nil,
|
|
64
|
+
byline: nil,
|
|
65
|
+
excerpt: nil,
|
|
66
|
+
site_name: nil,
|
|
67
|
+
published_time: nil,
|
|
68
|
+
canonical_url: nil,
|
|
69
|
+
language: nil,
|
|
70
|
+
html: nil,
|
|
71
|
+
markdown: "",
|
|
72
|
+
metadata: metadata,
|
|
73
|
+
reader_mode: nil,
|
|
74
|
+
content_type: "error",
|
|
75
|
+
suspect: true,
|
|
76
|
+
warnings: [warning],
|
|
77
|
+
error_message: message
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def payload_metadata(payload, canonical_url:, final_url:, content_type:, suspect:, warnings:,
|
|
84
|
+
content_completeness_ratio:, content_format:, paywall_state:)
|
|
85
|
+
{
|
|
86
|
+
title: payload["title"],
|
|
87
|
+
byline: payload["byline"],
|
|
88
|
+
excerpt: payload["excerpt"],
|
|
89
|
+
site_name: payload["siteName"],
|
|
90
|
+
published_time: payload["publishedTime"],
|
|
91
|
+
canonical_url: canonical_url,
|
|
92
|
+
language: payload["language"],
|
|
93
|
+
content_url: final_url,
|
|
94
|
+
reader_mode: payload["readerMode"],
|
|
95
|
+
content_type: content_type,
|
|
96
|
+
suspect: suspect,
|
|
97
|
+
warnings: warnings,
|
|
98
|
+
content_completeness_ratio: content_completeness_ratio,
|
|
99
|
+
content_format: content_format,
|
|
100
|
+
paywall_state: paywall_state
|
|
101
|
+
}.freeze
|
|
102
|
+
end
|
|
103
|
+
end
|
|
9
104
|
|
|
10
105
|
def initialize(url:, final_url:, title:, byline:, excerpt:, site_name:, published_time:,
|
|
11
106
|
canonical_url:, language:, html:, markdown:, metadata:, reader_mode:, content_type:, suspect:, warnings:,
|
|
12
|
-
content_completeness_ratio: 1.0, content_format: nil, paywall_state: nil)
|
|
107
|
+
content_completeness_ratio: 1.0, content_format: nil, paywall_state: nil, error_message: nil)
|
|
13
108
|
@url = url
|
|
14
109
|
@final_url = final_url
|
|
15
110
|
@title = title
|
|
@@ -29,6 +124,7 @@ module FetchUtil
|
|
|
29
124
|
@content_completeness_ratio = content_completeness_ratio
|
|
30
125
|
@content_format = content_format&.freeze
|
|
31
126
|
@paywall_state = paywall_state&.freeze
|
|
127
|
+
@error_message = error_message
|
|
32
128
|
end
|
|
33
129
|
|
|
34
130
|
def to_h
|
|
@@ -51,7 +147,8 @@ module FetchUtil
|
|
|
51
147
|
warnings: warnings,
|
|
52
148
|
content_completeness_ratio: content_completeness_ratio,
|
|
53
149
|
content_format: content_format,
|
|
54
|
-
paywall_state: paywall_state
|
|
150
|
+
paywall_state: paywall_state,
|
|
151
|
+
error_message: error_message
|
|
55
152
|
}
|
|
56
153
|
end
|
|
57
154
|
end
|
data/lib/fetch_util/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fetch_util
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- hmdne
|
|
@@ -82,6 +82,7 @@ files:
|
|
|
82
82
|
- lib/fetch_util/browser/navigation/navigator_patch.rb
|
|
83
83
|
- lib/fetch_util/browser/site_stabilization.rb
|
|
84
84
|
- lib/fetch_util/browser/site_stabilization/community_and_marketplace.rb
|
|
85
|
+
- lib/fetch_util/browser/site_stabilization/gitlab_repo.rb
|
|
85
86
|
- lib/fetch_util/browser/site_stabilization/social_platforms.rb
|
|
86
87
|
- lib/fetch_util/browser/stabilization.rb
|
|
87
88
|
- lib/fetch_util/browser/stabilization/page_flow.rb
|