From 302b5b0165327c1e366e5778cae359ac3d2035fc Mon Sep 17 00:00:00 2001 From: toom1996 <23cm.cn@gmail.com> Date: Tue, 27 Oct 2026 19:47:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=8B=E8=BD=BD=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E5=8F=96=20Vogue=20=E5=8E=9F=E5=9B=BE=EF=BC=88=E5=8E=BB?= =?UTF-8?q?=E6=8E=89=20master=20=E5=90=8E=E7=9A=84=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E6=AE=B5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xxl.url 是 .../master/w_2560,c_limit/,去掉缩放段即原图 .../master/。 w_3840 / w_6000 这类不带 c_limit 的参数是**上采样**(6000x9000 为插值), 文件更大但更糊,不能用——这点容易踩坑,已写进注释和单测。 实际收益有限,不是万能药: - 原图 < 2560 宽的老图:字节完全相同,纯属多一次跳转判断 - 新图(原图约 2732 宽):多拿 6.7% 宽度,带宽 +27% - 真正压糊图的是展示端 CDN 样式(720 宽),改这里不会让页面变清晰 --- internal/spider/original_url_test.go | 50 ++++++++++++++++++++++++++++ internal/spider/vogue.go | 38 +++++++++++++++++++-- 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 internal/spider/original_url_test.go diff --git a/internal/spider/original_url_test.go b/internal/spider/original_url_test.go new file mode 100644 index 0000000..c196dda --- /dev/null +++ b/internal/spider/original_url_test.go @@ -0,0 +1,50 @@ +package spider + +import "testing" + +// TestOriginalImageURL 固定「把缩放地址换成原图地址」的规则。 +// +// 这个函数直接决定下载什么,改错会整批图 404(抓取全失败),所以用测试钉住几种形态。 +func TestOriginalImageURL(t *testing.T) { + cases := []struct { + name string + in string + want string + }{ + { + name: "标准缩放地址", + in: "https://assets.vogue.com/photos/abc123/master/w_2560,c_limit/0001-x.jpg", + want: "https://assets.vogue.com/photos/abc123/master/0001-x.jpg", + }, + { + name: "已经是原图,不应改动", + in: "https://assets.vogue.com/photos/abc123/master/0001-x.jpg", + want: "https://assets.vogue.com/photos/abc123/master/0001-x.jpg", + }, + { + name: "pass 段也要去掉", + in: "https://assets.vogue.com/photos/abc123/master/pass/0001-x.jpg", + want: "https://assets.vogue.com/photos/abc123/master/0001-x.jpg", + }, + { + name: "非 master 路径(社交裁切图)保持原样", + in: "https://assets.vogue.com/photos/abc123/16:9/w_1000,c_limit/0001-x.jpg", + want: "https://assets.vogue.com/photos/abc123/16:9/w_1000,c_limit/0001-x.jpg", + }, + { + name: "空串保持空串", + in: "", + want: "", + }, + { + name: "其他域名保持原样", + in: "https://example.com/a/b/c.jpg", + want: "https://example.com/a/b/c.jpg", + }, + } + for _, c := range cases { + if got := originalImageURL(c.in); got != c.want { + t.Errorf("%s:\n 期望 %s\n 实际 %s", c.name, c.want, got) + } + } +} diff --git a/internal/spider/vogue.go b/internal/spider/vogue.go index 33102bc..4802ecc 100644 --- a/internal/spider/vogue.go +++ b/internal/spider/vogue.go @@ -442,7 +442,8 @@ func (s *VogueSpider) getDetail(brandUID string, info gjson.Result) { var mainImages []string for _, img := range imagesResult.Array() { - mainURL := img.Get("image.sources.xxl.url").String() + // 取原图而不是 xxl(w_2560 缩放档);见 originalImageURL 的注释。 + mainURL := originalImageURL(img.Get("image.sources.xxl.url").String()) if mainURL == "" { continue } @@ -450,7 +451,8 @@ func (s *VogueSpider) getDetail(brandUID string, info gjson.Result) { look := ingest.RunwayLook{Main: mainURL} for _, detail := range img.Get("details").Array() { - detailXxlURL := detail.Get("image.sources.xxl.url").String() + // 细节图同样取原图:细节图本来就是用来看细节的,压缩损失最明显。 + detailXxlURL := originalImageURL(detail.Get("image.sources.xxl.url").String()) if detailXxlURL != "" { look.Details = append(look.Details, detailXxlURL) } @@ -512,6 +514,38 @@ func (s *VogueSpider) getDetail(brandUID string, info gjson.Result) { } +// originalImageURL 把 Vogue 的「缩放地址」换成「原图地址」。 +// +// xxl.url 形如 `.../photos//master/w_2560,c_limit/<文件名>`; +// 去掉 master 后面的缩放段就是原图 `.../photos//master/<文件名>`。 +// +// 两点必须说清楚,否则很容易以为「改一下能清晰很多」: +// - `c_limit` 的语义是「不放大」,所以**老图(原图 < 2560 宽)两者字节完全相同**; +// - 新图原图约 2732 宽,换原图只多拿回 6.7% 的宽度,带宽却多 27%。 +// +// 真正把图压糊的是展示端(CDN 样式 720 宽),不是这里。 +// +// 另外:`w_3840` / `w_6000` 这类**不带 c_limit** 的参数会**上采样**(6000×9000 是插值出来的), +// 文件更大但更糊,不要用。只有去掉整段缩放才是原图。 +func originalImageURL(u string) string { + const marker = "/master/" + i := strings.Index(u, marker) + if i < 0 { + return u // 不是 master 路径,保持原样 + } + rest := u[i+len(marker):] + slash := strings.LastIndex(rest, "/") + if slash < 0 { + return u // master 后面直接是文件名,本来就是原图 + } + // 只去掉「看起来像缩放参数」的那一段,避免误伤带目录的路径。 + seg := rest[:slash] + if !strings.Contains(seg, "w_") && !strings.Contains(seg, "pass") && !strings.Contains(seg, ",") { + return u + } + return u[:i+len(marker)] + rest[slash+1:] +} + // parseVogueDate 容错解析 Vogue 的日期字符串:兼容完整 ISO 时间戳(含/不含时区、含小数秒) // 与「仅日期」格式(如 2015-08-08,老页面 publishDate 仅有日期)。解析失败返回零值。 func parseVogueDate(s string) (time.Time, bool) {