本例引用自https://blog.csdn.net/Mr__lqy/article/details/88753324,只适用于初学者,只需要会打断点追踪就可以了。
目标从网站获取视频链接,然后下载。
我这里使用的浏览器是chrome。
先打开网页链接
在开发者工具中找“Media”,这里可以看到一个请求地址
http://v3-default.ixigua.com/123236566489272e69198cde2756e341/5e0b075b/video/tos/hxsy/tos-hxsy-ve-0000/7507ec8b4de043ac94d23143c1c1162d/?a=2012&br=3446&bt=1723&cr=0&cs=0&dr=0&ds=3&er=&l=201912311530430100140640461E071D82&lr=motor&qs=0&rc=anJnM3B0N2x2aTMzO2gzM0ApNTk8aGU1ZmRlN2Y7Ozo6ZWdtXzVxX3FyNjNfLS0wLi9zcy0wYTU2XjVgNWI2L2I0X146Yw%3D%3D
它就是我们要找到目标地址。
按照原作者思路,先打开网页源码查看一下,是没有这个视频链接的。
注意,这里有段代码是有用的,就是下面的video_id 参数,回头会用到。
<script data-from="toutiao" crossorigin="anonymous">
if (self != top) {
top.location.href = self.location.href
}
//基础变量, 来自模板上下文
var group_id = "6615388842591518733";
var item_id = "6615388842591518733";
var request_id = "56716001577773371709";
var tag = "video_car";
var site_id = "5000246";
var weixinListPercent = 100;
var custom_proportion1 = 0;
var custom_proportion2 = 0;
var is_app_video_downgrade = 0;
var city = "上海";
var country_code = "CN";
var country = "中国";
var province = "上海";
var group_source = "20";
var is_ugc_video = true;
var content_type = "ugc_video";
var creator_uid = "100118509879";
var media_id = "1610842577106951";
var video_id = "v0201e380000bf6s9iu4tqbmt08dbopg";
</script>
继续,在开发者工具中Elements,可以看到视频的src
这说明这段video相关html代码是通过js动态创建的。
下面我们就需要跟踪html生成过程,最终找到链接地址的创建逻辑。
原作者是以html中的webkit-playsinline属性为突破口。
在开发者工具的Sources中搜索“webkit-playsinline”关键字
可以找到唯一相关代码,应该就是动态生成html的地方。
请注意9748行的videoList,从名称就应该感觉需要跟踪一下。
本文的第一个断点就是打在这里。
在9748行上用鼠标点一下,出现蓝色箭头,断点就已经打好了。
刷新页面,会自动停止在断点处。
最右边是断点,打勾表示有效。
调试代码时候,可以在控制台(Console)直接输出变量,看一下值。
上图中可以看到,videoList中已经加载好了2个src链接(分别是极速和超清)。
下面就需要追踪videoList的来源
把右边窗口滚动条网上移动,可以看到Call Stack,这里保留了代码运行过程的调用堆栈情况。
当前代码就是停留在最上面。
我们往下移动鼠标依次点击,可以逐步观察调用来自哪里。
移动2次后,可以到以下界面
有没有发现,videoList中的src,type,label等等都是在这里push进去的。
src来自t.main_url,而且被Base64编码了(因为这里用了base64decode解码)。关于Base64编码,参见链接。
我们继续往下移动堆栈2次,可以看到以下界面。
这里出现了数据,其中就包含了mail_url,当然是编码过的(解码后就可以得到本文最初提到的,也就是我们需要的视频目标地址)。
注意,鼠标移动到文件名称上,会出现调用地址。你可以鼠标右键,选择copy link address,就能得到下面的地址。
它有r,s,logo_type和callback参数
https://i.snssdk.com/video/urls/v/1/toutiao/mp4/v0201e380000bf6s9iu4tqbmt08dbopg?r=37066702101798144&s=315584556&logo_type=motor&callback=tt_playerdppez
现在再理一下思路,只要我们找到上面的链接地址(入口地址),就能获得main_url,然后把main_url解码,那就能获得视频的src了。
原作者此时是利用链接中的logo_type=motor进行追踪。
在sources中搜索logo_type=motor,可以找到唯一一处,我们在9785行打断点跟踪
此时,这里有一个crc32函数和2个参数r.config.remoteURL + r.config.videoID。
全局搜索remoteURL可以找到代码中定义的地方,它是一个定值。
remoteURL: "//i.snssdk.com/video/urls/v/1/toutiao/mp4/"
videoID其实已经出现过了,就是在本文最开始提到的页面源码中赋值。
var video_id = "v0201e380000bf6s9iu4tqbmt08dbopg"
打完断点后,刷新页面跟踪,可以进入crc32函数内部。
e.crc32 = function(e) {
var t = document.createElement("a");
t.href = e;
var n = function() {
for (var e = 0, t = new Array(256), n = 0; 256 != n; ++n)
e = n,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
e = 1 & e ? -306674912 ^ e >>> 1 : e >>> 1,
t[n] = e;
return "undefined" != typeof Int32Array ? new Int32Array(t) : t
}()
, r = function(e) {
for (var t, r, o = -1, i = 0, a = e.length; i < a; )
t = e.charCodeAt(i++),
t < 128 ? o = o >>> 8 ^ n[255 & (o ^ t)] : t < 2048 ? (o = o >>> 8 ^ n[255 & (o ^ (192 | t >> 6 & 31))],
o = o >>> 8 ^ n[255 & (o ^ (128 | 63 & t))]) : t >= 55296 && t < 57344 ? (t = (1023 & t) + 64,
r = 1023 & e.charCodeAt(i++),
o = o >>> 8 ^ n[255 & (o ^ (240 | t >> 8 & 7))],
o = o >>> 8 ^ n[255 & (o ^ (128 | t >> 2 & 63))],
o = o >>> 8 ^ n[255 & (o ^ (128 | r >> 6 & 15 | (3 & t) << 4))],
o = o >>> 8 ^ n[255 & (o ^ (128 | 63 & r))]) : (o = o >>> 8 ^ n[255 & (o ^ (224 | t >> 12 & 15))],
o = o >>> 8 ^ n[255 & (o ^ (128 | t >> 6 & 63))],
o = o >>> 8 ^ n[255 & (o ^ (128 | 63 & t))]);
return o ^ -1
}
, o = t.pathname + "?r=" + Math.random().toString(10).substring(2);
"/" != o[0] && (o = "/" + o);
var i = r(o) >>> 0;
return [t.protocol, t.hostname].join("//") + o + "&s=" + i
}
这个函数就是用来生成部分目标链接地址的。
地址前面部分就不解释了,后面r参数和s参数都是在这里计算获得的。
而logo_type参数在9787行赋值,是写死的。
继续往下跟踪几步,进入9788行后,可以到达以下界面
这里出现了一个函数,送入"tt_player",获得tt_player加一个随机字符串
e.getName = function(e) {
return e + Math.random().toString(36).replace(/[^a-z]+/g, "").substr(0, 5)
}
最终callback参数在以下代码中拼接完成
var a = e.createScript(t + "&callback=" + i, o)
送入createScript中创建js。
至此,url地址逻辑已经全部得到。
总结一下:
我们需要的是remoteURL和video_id的值,crc32函数获得r和s的值,getName函数获得callback的值,把这些内容拼接起来得到请求地址。
通过请求地址可以获得tt_player开头的字符串。
在这个字符串中截取mai_url的值(有2个),经过Base64位解码,就能得到最终的目标地址了。
再用目标地址就可以直接下载感兴趣的视频。
完整代码参见链接。