0x00 开篇
一直都在写教程,今天咱们换换主题,一起来做一个小工具。咱们用 Rust 来写一个抖音短视频下载地址解析工具。本文用到异步、网络请求等相关知识。另外公众号官方社群(文末)也开放了,欢迎大家加入,一起畅谈 Rust 的未来。本篇文章的阅读时间大约 8 分钟。
0x01 视频解析原理
原理其实很简单,每个短视频对应一个 id,我们将 id 传给官方的 api
地址:https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids={id} 可以得到当前短视频一些信息,如:视频名称,封面图片,音频地址,视频地址等等。这其中的视频地址就是可以下载的地址了。
以id = 7162104318923967752
为例,请求的信息如下:
最终 video/url_list
中的值就是我们想要的了。
0x02 代码实现
添加第三方 crate 依赖
本篇文章将使用下面的依赖:
- 异步:tokio
- 网络请求:reqwest
- json 解析: serde
# 异步 tokio = { version = "1.21.2", features = ["full"] } # 网络 reqwest = {version = "0.11.12", features = ["json"] } # json serde_json = "1.0" serde = { version = "1.0", features = ["derive"] }
获取短视频 id
首先,我们通过用户输入的地址,获取短视频 id。短视频的地址都是如 https://v.douyin.com/xxxxxxx这种形式。当我们在浏览器请求此链接时,将会重定向为一个新的地址,我们所需的 id
就在新的地址中。
请求接下来以 https://v.douyin.com/h1YcUow 为例。
直接上代码了:
let mut input = String::new(); // 输入 https://v.douyin.com/h1YcUow std::io::stdin().read_line(&mut input).expect("read line error!"); // 获取 response let result = create_default_client().get(input.as_str()).send().await; let result = result.unwrap(); dbg(&result); // 获取重定向后的地址,并解析id let path = result.url().path(); let id = path.replace("/video/", "");
用户先输入短视频分享的链接https://v.douyin.com/h1YcUow, 然后请求这个链接,通过返回结果 response
中的 path
获取 id。下面是 response
的结果。
&result = Response { url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some( Domain( "www.douyin.com", ), ), port: None, // 7162104318923967752 就是我们所需的 id path: "/video/7162104318923967752", query: Some( "previous_page=web_code_link", ), fragment: None, }, ..... }
通过 id 拼接链接并请求
// 请求拼接请求地址 let url = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids="; let url = format!("{}{}", url, id); // 请求该链接 let result = create_default_client().get(url.as_str()).send().await; // 结果转化为结构体 let result = result.unwrap().json::<DouYinResult>().await;
将 id
与链接拼接并请求,将请求结果通过 serde
转为 Rust 相对应的结构体。(DouYinResult
请参见源码)。
解析结果
let dou_yin_items = result.item_list.unwrap(); let dou_yin_item = dou_yin_items.into_iter().nth(0).unwrap(); // 视频名称 let video_name = dou_yin_item.desc.unwrap_or_default(); // 视频地址 let dou_yin_video = dou_yin_item.video.unwrap(); // 获取 play_address let play_address = dou_yin_video.play_addr.unwrap(); // 获取 play_list let play_list = play_address.url_list.unwrap_or_default(); // video url let video_url = play_list.into_iter().nth(0).unwrap_or_default();
由于篇幅有限,上面解析的代码使用了大量 unwrap(),详细代码请参阅源码,项目中不建议大量使用 unwrap()。我们最终拿到的地址如下:
这个地址就可以下载了。但是有个缺点,这个地址有水印,如果想要去掉水印,我们把地址中的 playwm 替换为 play就可以了。
最终地址:
https://aweme.snssdk.com/aweme/v1/play/?video_id=v0200fg10000cdieeejc77ucgndnburg&ratio=720p&line=0
获取最终视频源地址
其实到上面一步已经可以正常下载了,如果我们将上面的地址再请一次,链接会重定向到最终的视频地址,代码详见源码:
这个地址可以在任意浏览器或者下载工具中下载了。
0x03 小结
原理和代码都还算比较容易,用到的接口也是官方接口,失效的几率很低,大概率可以长期使用。通过测试我也发现并不是所有的视频都可以下载,极少数视频无法下载。大家如果感兴趣可以下载源码并完善。但是是不是如果有GUI界面就好了,文章还没有结束,接下来我将会结合 tauri
来完成一个跨平台的短视频解析工具。