fix(eh): 自动放行 EH 的 Content Warning 拦截页
现象:部分画廊(多被标成 Offensive For Everyone)解析必然失败,报 「页面结构无法解析(EH 可能又改版)」——这条报错是误导的,跟改版无关。 根因:EH 对这类画廊第一次访问只返回一个 3~4KB 的黄框拦截页, <title> 和 meta 都在,但一个 /s/ 链接都没有,正则自然一条都匹配不上。 放行需要请求一次 ?nw=always,而 EH 靠下发的 nw=1 Cookie 记住 「别再警告」——可 Client 根本没设 CookieJar,Cookie 下发即丢, 于是永远过不去。 实测:4166903 / 4021921 / 3799635 / 3627469 四个画廊修复后 分别解析出 45/95/36/73 页,与站点声明完全一致。 改动: - Client 持有 cookiejar 并挂到 http.Client.Jar (Go 1.26 起 http.Transport 已经没有 Jar 字段了,只能挂 Client) - 新增 getTextAutoWarn:撞上拦截页自动 ?nw=always 放行一次再重试, nw=1 进 jar 后全站不再警告 - 放行失败时的报错明说是内容警告,并提示去浏览器点一次, 不再甩锅给「EH 可能又改版」
This commit is contained in:
+48
-2
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -280,6 +281,11 @@ type Client struct {
|
||||
|
||||
transport *http.Transport // 全局共用,只有代理配置变了才重建
|
||||
limiter *Limiter
|
||||
|
||||
// jar 必须有:EH 对部分画廊先弹「Content Warning」拦截页,
|
||||
// 放行靠的是它下发的 nw=1 Cookie。没有 jar 的话每次请求都是
|
||||
// 无状态连接,Cookie 下发即丢,于是永远过不去(2026-10 修)。
|
||||
jar http.CookieJar
|
||||
}
|
||||
|
||||
// NewClient 建客户端
|
||||
@@ -290,12 +296,14 @@ func NewClient(timeout time.Duration, proxyURL, proxyMode, xres string) *Client
|
||||
if xres == "" {
|
||||
xres = "auto"
|
||||
}
|
||||
jar, _ := cookiejar.New(nil) // cookiejar.New 只在 nil options 时出错
|
||||
return &Client{
|
||||
timeout: timeout,
|
||||
proxy: proxyURL,
|
||||
proxyMode: proxyMode,
|
||||
xres: xres,
|
||||
limiter: NewLimiter(0),
|
||||
jar: jar,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +371,9 @@ func (c *Client) httpClient(timeout time.Duration) *http.Client {
|
||||
if timeout <= 0 {
|
||||
timeout = defTimeout
|
||||
}
|
||||
return &http.Client{Transport: tr, Timeout: timeout}
|
||||
// Jar 挂在 Client 上(Go 1.26 起 http.Transport 已经没有 Jar 字段了),
|
||||
// 少一个 jar 就会退回无状态模式,EH 的 nw=1 记不住。
|
||||
return &http.Client{Transport: tr, Timeout: timeout, Jar: c.jar}
|
||||
}
|
||||
|
||||
// rebuildTransport 按当前代理配置重建 Transport,并关掉旧连接的空闲池
|
||||
@@ -457,6 +467,37 @@ func (c *Client) getText(ctx context.Context, u string) (string, error) {
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// reContentWarning EH 的「Content Warning」拦截页特征。
|
||||
//
|
||||
// 这类画廊(多被标成 Offensive For Everyone)第一次访问只会拿到一个
|
||||
// 3~4KB 的黄框页,标题和 meta 都在,但**一个 /s/ 链接都没有**,
|
||||
// 于是旧代码直接报「页面结构无法解析(EH 可能又改版)」——纯误导。
|
||||
var reContentWarning = regexp.MustCompile(`(?i)<h1>\s*Content Warning\s*</h1>|nw=always`)
|
||||
|
||||
func isContentWarning(html string) bool {
|
||||
return reContentWarning.MatchString(html)
|
||||
}
|
||||
|
||||
// getTextAutoWarn 抓文本;撞上内容警告页就自动放行一次再重试。
|
||||
//
|
||||
// 放行方式是请求一次 ?nw=always —— EH 会种下 nw=1 Cookie,
|
||||
// 而 jar 会把它记住,之后所有画廊页、/s/ 页都不用再警告。
|
||||
func (c *Client) getTextAutoWarn(ctx context.Context, u string) (string, error) {
|
||||
html, err := c.getText(ctx, u)
|
||||
if err != nil || !isContentWarning(html) {
|
||||
return html, err
|
||||
}
|
||||
|
||||
sep := "?"
|
||||
if strings.Contains(u, "?") {
|
||||
sep = "&"
|
||||
}
|
||||
if _, werr := c.getText(ctx, u+sep+"nw=always"); werr != nil {
|
||||
return html, nil // 放行失败就原样返回,交给调用方判断
|
||||
}
|
||||
return c.getText(ctx, u)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 1) 画廊页
|
||||
|
||||
// ParseProgress 解析进度回调
|
||||
@@ -489,7 +530,7 @@ func (c *Client) FetchGalleryPage(ctx context.Context, gid, token string, onProg
|
||||
if p > 0 {
|
||||
u += "?p=" + strconv.Itoa(p)
|
||||
}
|
||||
html, err := c.getText(ctx, u)
|
||||
html, err := c.getTextAutoWarn(ctx, u)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析画廊页失败: %w", err)
|
||||
}
|
||||
@@ -552,6 +593,11 @@ func (c *Client) FetchGalleryPage(ctx context.Context, gid, token string, onProg
|
||||
if reRestricted.MatchString(lastHTML) {
|
||||
return nil, fmt.Errorf("画廊受限或不存在(无 cookie 访问不了): %s", gid)
|
||||
}
|
||||
if isContentWarning(lastHTML) {
|
||||
return nil, fmt.Errorf(
|
||||
"画廊被 EH 标记为「Offensive For Everyone」,自动放行没成功"+
|
||||
"(请在浏览器里打开一次并点 Never Warn Me Again): %s", gid)
|
||||
}
|
||||
return nil, fmt.Errorf("页面结构无法解析(EH 可能又改版): %s", gid)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user