ask-web-fetch 0.6.1 → 0.6.2

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: 15d4465ae7ca7805003043af5b97e96ddc8c2ba19618daab313377f4a529bd54
4
- data.tar.gz: 9f83f8c2258dc973457158e7d7e2288afc43403221453545a4d19c97440b85ee
3
+ metadata.gz: 5e7b287b5b62f821cb0b3085082b385583d3f1a7ef2dfc0816eb311931d1e218
4
+ data.tar.gz: 534bce458033fb046914c169b7bb7bad982f8fcd3ffb999d6feb2b495953d90c
5
5
  SHA512:
6
- metadata.gz: b34dfd5f3d45b827cf8882d3c9c3b47e05ea8c16622849b2a41fc51b8c5f005f0b149ef83a87db57f4167470e85911ec58f72830afded9a84ab707ea08dcbc68
7
- data.tar.gz: c8245c574d480179cd3b8ad67f1049b096fdf298f0886a790cb557f698c11e8dbaed9bb4336de4ccf84882e03eb8cf716830db069988f310c35cf4202cf01d60
6
+ metadata.gz: 48e47dd345f498f1a50b6dc3ddb09a5f55078691ee34a617b02936fb9b7d639c2ff55c309f6fc2ecec2a4d067c487edd0975c983b4ed301471d50536e73e6bd0
7
+ data.tar.gz: 7bf1c58c8fb17a9c5575b41f8b2d44c6deb1c01a8b82b56b3d73f5d1df4c5d72fadf09ca60de1fc3995b8dc62581611f4aa50bcb4c47ceb9894f5bb6749490fc
@@ -44,7 +44,9 @@ module Ask
44
44
  # Markdown.generate, backends fed pre-converted markdown (Jina,
45
45
  # Crawl4AI) must call it explicitly so the shared noise removal
46
46
  # and whitespace normalization apply everywhere
47
- # 5. register the class in Ask::Tools::WebFetch.backends
47
+ # 5. run the extracted page through #guard_page! — the parked-domain
48
+ # and empty-content verdicts are identical in every backend
49
+ # 6. register the class in Ask::Tools::WebFetch.backends
48
50
  #
49
51
  # The tool tries each backend in order and returns the first success.
50
52
  class Backend
@@ -92,6 +94,29 @@ module Ask
92
94
  raise NotImplementedError, "#{self.class} must implement #fetch(url)"
93
95
  end
94
96
 
97
+ # The shared page guard, run by EVERY backend at the same point in
98
+ # its flow — after extraction, before returning: a registrar parking
99
+ # page raises ParkedDomainError, content below the minimum raises
100
+ # EmptyContentError. Same verdicts, same messages, everywhere; a
101
+ # backend's only job is to pass the strings it has.
102
+ #
103
+ # raw_body: the raw HTML the backend saw, where it saw it (Local,
104
+ # Browser) — the HTML-only markers (ap:"parking", parking-lander,
105
+ # LANDER_SYSTEM="PW") live in scripts and assets that never survive
106
+ # conversion to markdown. content: what the backend would return
107
+ # (all four) — the prose markers survive conversion, so a backend
108
+ # that only ever sees rendered text (Jina, Crawl4AI) still rejects
109
+ # the ad.
110
+ #
111
+ # Parked is checked BEFORE the content minimum on purpose: a parking
112
+ # page can render above it (puncta.ai: 395c of Namecheap auction
113
+ # ads) and must still be rejected.
114
+ def guard_page!(url, content, raw_body: nil)
115
+ raise ParkedDomainError,
116
+ "parked domain at #{url} — registrar parking page, not site content" if parked_domain?(raw_body) || parked_domain?(content)
117
+ raise EmptyContentError, "no readable content at #{url}" unless usable_content?(content)
118
+ end
119
+
95
120
  # --- outlinks (crawler discovery) ---
96
121
 
97
122
  # The page's raw outlinks from HTML: every <a href> resolved against
@@ -201,13 +201,12 @@ module Ask
201
201
  end
202
202
  # Browser renders the parked page a JS redirect lands on (the
203
203
  # server shell hands /lander to JS) — Local never sees it. The
204
- # shared parked-domain detector catches it here; the distinct
205
- # ParkedDomainError lets the pipeline classify (never retry) it.
206
- raise ParkedDomainError, "parked domain at #{url} registrar parking page, not site content" if parked_domain?(body)
207
-
204
+ # shared guard catches it on the rendered HTML (raw_body) and
205
+ # the converted content; the distinct ParkedDomainError lets the
206
+ # pipeline classify (never retry) it.
208
207
  result = Markdown.generate(body, base_url: url, filter: self.class.content_filter)
209
208
  result[:outlinks] = outlink_urls(body, url)
210
- raise EmptyContentError, "no readable content at #{url}" unless usable_content?(result[:content])
209
+ guard_page!(url, result[:content], raw_body: body)
211
210
 
212
211
  result
213
212
  end
@@ -56,12 +56,12 @@ module Ask
56
56
 
57
57
  page = to_page(body, url)
58
58
  # A registrar parking page renders fine in headless Chrome too —
59
- # the shared detector (0.5.7) catches the text markers that
60
- # survive conversion, so the ad is rejected, not returned as
61
- # the site's content. Crawl4AI leads the default chain, so this
62
- # check is what keeps parked domains out of every result.
63
- raise ParkedDomainError, "parked domain at #{url} registrar parking page, not site content" if parked_domain?(page[:content])
64
- raise EmptyContentError, "no readable content at #{url}" unless usable_content?(page[:content])
59
+ # the shared guard's prose markers catch it (the HTML-only
60
+ # markers never reach a markdown-only backend), so the ad is
61
+ # rejected, not returned as the site's content. Crawl4AI leads
62
+ # the default chain, so this guard is what keeps parked domains
63
+ # out of every result.
64
+ guard_page!(url, page[:content])
65
65
 
66
66
  page
67
67
  rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED,
@@ -35,18 +35,17 @@ module Ask
35
35
  body = res.body.to_s
36
36
  raise FetchError, 'challenge page from Jina' if challenge_page?(body)
37
37
 
38
+ content = Markdown.clean(body)
38
39
  # Jina only sees rendered markdown — outlinks come from its
39
40
  # links, resolved against the requested URL. Content runs
40
41
  # through the same Markdown.clean as the converting backends,
41
42
  # so decorative symbol noise is stripped here too; a page
42
43
  # whose only "content" was noise falls through as empty. A
43
44
  # registrar parking page renders fine through Jina — the
44
- # shared detector (0.5.7) catches the text markers that
45
- # survive conversion, so the ad is rejected, not returned as
46
- # the site's content.
47
- content = Markdown.clean(body)
48
- raise ParkedDomainError, "parked domain at #{url} — registrar parking page, not site content" if parked_domain?(content)
49
- raise EmptyContentError, 'empty response from Jina' unless usable_content?(content)
45
+ # shared guard's prose markers catch it (the HTML-only
46
+ # markers never reach a markdown-only backend), so the ad is
47
+ # rejected, not returned as the site's content.
48
+ guard_page!(url, content)
50
49
 
51
50
  { title: nil, description: nil, content: content, outlinks: markdown_outlinks(body, url) }
52
51
  when '429'
@@ -48,16 +48,12 @@ module Ask
48
48
  # Parked-domain pages are not content: the domain owner parked it
49
49
  # with a registrar and the page is an ad for buying the domain
50
50
  # (GoDaddy/Namecheap/Sedo parking). A content company must never
51
- # store these as if they were the site. Checked BEFORE the
52
- # content-minimum a parking page can render as "content" above
53
- # the minimum (puncta.ai: 395c of Namecheap auction ads), and
54
- # must still be rejected. Detectable from the server HTML —
55
- # parked pages are fully server-rendered, so both Local and
56
- # Browser see the same ad.
57
- if parked_domain?(body)
58
- raise ParkedDomainError, "parked domain at #{url} — registrar parking page, not site content"
59
- end
60
- raise EmptyContentError, "no readable content at #{url}" unless usable_content?(page[:content])
51
+ # store these as if they were the site. The shared guard checks
52
+ # the raw server HTML first (parked pages are fully
53
+ # server-rendered the HTML-only markers live in scripts and
54
+ # assets), then the content minimum, then the JS-shell
55
+ # completeness signal below.
56
+ guard_page!(url, page[:content], raw_body: body)
61
57
  # The completeness signal: a JS-app shell whose server HTML
62
58
  # renders little is a TRUNCATED page, not a complete one — the
63
59
  # real content awaits client-side JS that Local cannot run.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module WebFetch
5
- VERSION = '0.6.1'
5
+ VERSION = '0.6.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-web-fetch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto