Drop LWN subscriber-only articles instead of stamping paywalled
LWN's subscriber-only articles have no public version reachable by archive snapshots or bypass UAs, so stamping them as paywalled produces clicks that always dead-end. Detect the marker text and skip ingestion entirely.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -68,7 +69,12 @@ type ArticleMeta struct {
|
||||
Fetched bool // true if we got an HTTP 200 with HTML
|
||||
FetchError bool // true if the fetch failed at the network/HTTP layer
|
||||
Paywalled bool // true if the page explicitly declares gated access
|
||||
Status int // last HTTP status seen (0 on transport error)
|
||||
// SubscriberOnly is true when the source publishes the article in a
|
||||
// form that genuinely has no public version — no archive snapshot, no
|
||||
// bypass UA, nothing. Callers should drop these from view entirely
|
||||
// rather than stamp them as paywalled.
|
||||
SubscriberOnly bool
|
||||
Status int // last HTTP status seen (0 on transport error)
|
||||
}
|
||||
|
||||
// Gated reports whether the response carries a strong gating signal: an
|
||||
@@ -107,6 +113,9 @@ func shouldRetryAsBot(m ArticleMeta) bool {
|
||||
if m.FetchError {
|
||||
return false // transport failure won't be fixed by a different UA
|
||||
}
|
||||
if m.SubscriberOnly {
|
||||
return false // genuinely subscriber-only; no UA changes that
|
||||
}
|
||||
if m.Gated() {
|
||||
return true
|
||||
}
|
||||
@@ -185,9 +194,27 @@ func fetchArticleMetaOnce(articleURL, ua, referer string) ArticleMeta {
|
||||
meta.ImageURL = extractOGImage(doc, articleURL)
|
||||
meta.BodyChars = extractBodyChars(doc)
|
||||
meta.Paywalled = detectPaywall(doc)
|
||||
meta.SubscriberOnly = detectSubscriberOnly(articleURL, doc)
|
||||
return meta
|
||||
}
|
||||
|
||||
// detectSubscriberOnly returns true for sources known to publish articles
|
||||
// with no public version reachable by any workaround we have (archive
|
||||
// snapshots, bypass UAs, etc). Currently only LWN's subscriber-only
|
||||
// articles, which display a fixed marker in the article body.
|
||||
func detectSubscriberOnly(articleURL string, doc *goquery.Document) bool {
|
||||
u, err := url.Parse(articleURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
host := strings.ToLower(u.Hostname())
|
||||
if host != "lwn.net" && !strings.HasSuffix(host, ".lwn.net") {
|
||||
return false
|
||||
}
|
||||
body := strings.ToLower(doc.Find("body").Text())
|
||||
return strings.Contains(body, "available only to lwn subscribers")
|
||||
}
|
||||
|
||||
// isCloudflareChallenge reports whether a non-200 response is Cloudflare's
|
||||
// bot-block or JS-challenge page rather than a real publisher gate. The
|
||||
// signatures are stable: cf-* response headers, the cf-error-details body
|
||||
|
||||
Reference in New Issue
Block a user