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.
@@ -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 = clean_text(fragment_title(document, final_url) || first_heading(root) || meta_title(document) || document.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
- node = document.at_css(selector)
154
- return node.dup if node && clean_text(node.text).length >= 120
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FetchUtil
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
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.0
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