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:
50
internal/spider/original_url_test.go
Normal file
50
internal/spider/original_url_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -442,7 +442,8 @@ func (s *VogueSpider) getDetail(brandUID string, info gjson.Result) {
|
|||||||
var mainImages []string
|
var mainImages []string
|
||||||
|
|
||||||
for _, img := range imagesResult.Array() {
|
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 == "" {
|
if mainURL == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -450,7 +451,8 @@ func (s *VogueSpider) getDetail(brandUID string, info gjson.Result) {
|
|||||||
|
|
||||||
look := ingest.RunwayLook{Main: mainURL}
|
look := ingest.RunwayLook{Main: mainURL}
|
||||||
for _, detail := range img.Get("details").Array() {
|
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 != "" {
|
if detailXxlURL != "" {
|
||||||
look.Details = append(look.Details, 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 时间戳(含/不含时区、含小数秒)
|
// parseVogueDate 容错解析 Vogue 的日期字符串:兼容完整 ISO 时间戳(含/不含时区、含小数秒)
|
||||||
// 与「仅日期」格式(如 2015-08-08,老页面 publishDate 仅有日期)。解析失败返回零值。
|
// 与「仅日期」格式(如 2015-08-08,老页面 publishDate 仅有日期)。解析失败返回零值。
|
||||||
func parseVogueDate(s string) (time.Time, bool) {
|
func parseVogueDate(s string) (time.Time, bool) {
|
||||||
|
|||||||
Reference in New Issue
Block a user