作为一名抖音内容创作者,我最近遇到了一个让人抓狂的问题:当我想要把早期发布的视频添加到新创建的合集时,发现抖音APP的操作流程极其低效。系统只提供了"从全部作品中添加视频"的入口,而且已经添加到其他合集的作品仍然会显示出来,却无法再次选择(因为一个作品只能属于一个合集)。每次都要在几百个视频中一页页翻找,耗时又费力。
更让人不解的是,同类平台如小红书在给合集添加作品时,会自动过滤掉已经在其他合集的作品。这种基础功能在抖音上竟然缺失,实在令人费解。作为一个有技术背景的创作者,我决定自己动手解决这个问题。
通过浏览器开发者工具分析抖音网页版的网络请求,我发现了三个关键API接口:
/aweme/v1/web/aweme/post//aweme/v1/web/mix/list//aweme/v1/web/mix/aweme/这些接口需要携带Cookie和一系列签名参数(如a_bogus、msToken等),虽然时效性较短,但可以从浏览器中直接复制使用。
选择Go语言开发这个工具主要基于以下考虑:
项目架构设计如下:
为了简化配置过程,我设计了两种配置方式:
自动解析模式的实现关键点:
go复制func parseMixListURL() error {
parsedURL, err := url.Parse(config.MixListURL)
if err != nil {
return err
}
query := parsedURL.Query()
config.BaseParams = make(map[string]string)
for k, v := range query {
config.BaseParams[k] = v[0]
}
config.SecUserID = config.BaseParams["sec_user_id"]
return nil
}
这种设计大大降低了使用门槛,用户只需从浏览器复制一个URL即可完成配置。
合集列表接口采用分页设计,通过cursor参数控制翻页。核心实现逻辑:
go复制func FetchAllMixes(client *http.Client) ([]MixInfo, error) {
var mixes []MixInfo
cursor := 0
for {
urlStr := buildURL(basePath, map[string]string{
"sec_user_id": config.SecUserID,
"cursor": strconv.Itoa(cursor),
"count": "20",
"list_scene": "3",
})
// 发送请求并处理响应
if resp.HasMore == 0 {
break
}
cursor = resp.Cursor
time.Sleep(time.Duration(config.DelayMs) * time.Millisecond)
}
return mixes, nil
}
为了提高效率,我实现了带并发限制的goroutine池:
go复制func fetchAllMixVideosConcurrent(client *http.Client, mixes []MixInfo, concurrency int) ([]MixVideo, error) {
type result struct {
videos []MixVideo
err error
}
ch := make(chan result, len(mixes))
sem := make(chan struct{}, concurrency)
for _, mix := range mixes {
go func(m MixInfo) {
sem <- struct{}{}
defer func() { <-sem }()
videos, err := FetchMixVideos(client, m.MixID, m.MixName)
ch <- result{videos: videos, err: err}
}(mix)
}
// 结果收集逻辑...
return allVideos, nil
}
这种设计可以在不触发平台风控的前提下,显著提高数据获取速度。
获取到所有作品和合集数据后,需要进行比对分析,找出未归类到任何合集的作品。关键实现点:
导出功能使用excelize库实现,特别注意了列宽设置和格式优化:
go复制func ExportAllAwemes(awemes []Aweme) error {
f := excelize.NewFile()
// 设置列宽
f.SetColWidth(sheetName, "A", "A", 30) // 作品ID
f.SetColWidth(sheetName, "B", "B", 50) // 作品内容
f.SetColWidth(sheetName, "F", "F", 80) // 播放地址
// 添加空白列防止最后一列过度拉伸
f.SetColWidth(sheetName, "H", "H", 10)
// 写入数据逻辑...
return f.SaveAs(filepath.Join(exportDir, "all_awemes.xlsx"))
}
YAML格式问题:
签名过期问题:
并发控制:
在实际使用过程中,我发现还可以进一步优化:
这个项目已经开源在我的Gitee仓库,欢迎有兴趣的开发者一起完善。通过这个案例,我深刻体会到技术可以如何解决实际生活中的痛点问题。作为开发者,我们应该多观察生活中的不便之处,用技术创造更高效的工具。