feat: 下载改为取 Vogue 原图(去掉 master 后的缩放段)

xxl.url 是 .../master/w_2560,c_limit/<file>,去掉缩放段即原图 .../master/<file>。
w_3840 / w_6000 这类不带 c_limit 的参数是**上采样**(6000x9000 为插值),
文件更大但更糊,不能用——这点容易踩坑,已写进注释和单测。

实际收益有限,不是万能药:
- 原图 < 2560 宽的老图:字节完全相同,纯属多一次跳转判断
- 新图(原图约 2732 宽):多拿 6.7% 宽度,带宽 +27%
- 真正压糊图的是展示端 CDN 样式(720 宽),改这里不会让页面变清晰
This commit is contained in:
toom1996
2026-10-27 19:47:44 +08:00
parent 463a57c497
commit 302b5b0165
2 changed files with 86 additions and 2 deletions

View File

@ -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)
}
}
}

View File

@ -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/<id>/master/w_2560,c_limit/<文件名>`;
// 去掉 master 后面的缩放段就是原图 `.../photos/<id>/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) {