fix(recruitment): fetchHTML curl 优先 + https 归一化 + 调试日志改 warning 级(logger.level=warning 过滤了 Infof)
Some checks failed
Build and Deploy (service.xpcool.com) / build-and-deploy (push) Failing after 32s
Some checks failed
Build and Deploy (service.xpcool.com) / build-and-deploy (push) Failing after 32s
This commit is contained in:
parent
8b11893d35
commit
8721d01028
@ -79,10 +79,10 @@ func genericStaticCrawl(ctx context.Context, src entity.CrawlSource, force bool)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
g.Log().Infof(ctx, "[recruit-debug] src=%d listURL=%s htmlLen=%d", src.Id, listURL, len(html))
|
g.Log().Warningf(ctx, "[recruit-debug] src=%d listURL=%s htmlLen=%d", src.Id, listURL, len(html))
|
||||||
raw := extractAnchors(html, listURL)
|
raw := extractAnchors(html, listURL)
|
||||||
anchors := filterArticleAnchors(raw)
|
anchors := filterArticleAnchors(raw)
|
||||||
g.Log().Infof(ctx, "[recruit-debug] src=%d rawAnchors=%d filtered=%d", src.Id, len(raw), len(anchors))
|
g.Log().Warningf(ctx, "[recruit-debug] src=%d rawAnchors=%d filtered=%d", src.Id, len(raw), len(anchors))
|
||||||
var out []dto.RecruitmentInput
|
var out []dto.RecruitmentInput
|
||||||
limit := 60
|
limit := 60
|
||||||
if force {
|
if force {
|
||||||
@ -189,10 +189,17 @@ func walkJSON(node any, out *[]dto.RecruitmentInput) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchHTML 带超时与浏览器 UA 的请求,降低被反爬概率。
|
// fetchHTML 抓取页面。优先用系统 curl(已实测:政府站在容器内对 curl 返回完整页面,
|
||||||
// 部分政府站对 Go HTTP 客户端做 TLS 指纹级拦截(返回无锚点页面),
|
// 而 Go 客户端可能被 TLS 指纹级 WAF 拦截返回无内容挑战页),失败再回退 Go 客户端。
|
||||||
// 此时回退系统 curl 重试(容器镜像内置 curl,已验证可拿到完整列表页)。
|
|
||||||
func fetchHTML(ctx context.Context, u string) (string, error) {
|
func fetchHTML(ctx context.Context, u string) (string, error) {
|
||||||
|
if body, e := fetchWithCurl(u); e == nil && looksLikeHtml(body) && len(body) >= 3000 {
|
||||||
|
g.Log().Warningf(ctx, "[recruit-debug] curl ok len=%d url=%s", len(body), u)
|
||||||
|
return body, nil
|
||||||
|
} else if e != nil {
|
||||||
|
g.Log().Warningf(ctx, "[recruit-debug] curl failed: %v url=%s", e, u)
|
||||||
|
} else {
|
||||||
|
g.Log().Warningf(ctx, "[recruit-debug] curl suspicious len=%d url=%s", len(body), u)
|
||||||
|
}
|
||||||
client := g.Client()
|
client := g.Client()
|
||||||
client.SetTimeout(15 * time.Second)
|
client.SetTimeout(15 * time.Second)
|
||||||
client.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36")
|
client.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36")
|
||||||
@ -200,27 +207,18 @@ func fetchHTML(ctx context.Context, u string) (string, error) {
|
|||||||
client.SetHeader("Accept-Language", "zh-CN,zh;q=0.9")
|
client.SetHeader("Accept-Language", "zh-CN,zh;q=0.9")
|
||||||
resp, err := client.Get(ctx, u)
|
resp, err := client.Get(ctx, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 网络层失败:尝试 curl 兜底,避免单点失败。
|
|
||||||
if body, e := fetchWithCurl(u); e == nil {
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
return "", gerror.Wrapf(err, "fetch %s", u)
|
return "", gerror.Wrapf(err, "fetch %s", u)
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
// 非 200(如 302/403):回退 curl 重试(curl 自动跟随重定向)。
|
|
||||||
if body, e := fetchWithCurl(u); e == nil {
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
return "", gerror.Newf("HTTP %d for %s", resp.StatusCode, u)
|
return "", gerror.Newf("HTTP %d for %s", resp.StatusCode, u)
|
||||||
}
|
}
|
||||||
body := resp.ReadAllString()
|
body := resp.ReadAllString()
|
||||||
// 内容可疑(疑似 WAF/JS 挑战页:无 html 特征或锚点过少)时回退 curl。
|
|
||||||
if !looksLikeHtml(body) || len(body) < 3000 {
|
if !looksLikeHtml(body) || len(body) < 3000 {
|
||||||
if cb, e := fetchWithCurl(u); e == nil {
|
g.Log().Warningf(ctx, "[recruit-debug] go-fallback suspicious len=%d url=%s", len(body), u)
|
||||||
return cb, nil
|
return "", gerror.Newf("suspicious page (len=%d) for %s", len(body), u)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
g.Log().Warningf(ctx, "[recruit-debug] go-fallback ok len=%d url=%s", len(body), u)
|
||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +419,12 @@ func resolveURL(base, href string) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return u.ResolveReference(ref).String()
|
abs := u.ResolveReference(ref).String()
|
||||||
|
// 同一政府页面常同时含 http/https 两种文章链接,统一升级为 https 便于去重。
|
||||||
|
if strings.HasPrefix(base, "https://") && strings.HasPrefix(abs, "http://") {
|
||||||
|
abs = "https://" + strings.TrimPrefix(abs, "http://")
|
||||||
|
}
|
||||||
|
return abs
|
||||||
}
|
}
|
||||||
|
|
||||||
// joinURL 拼接基础域名与路径。
|
// joinURL 拼接基础域名与路径。
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user