0. 介绍

本教程根据stakoverflow 回答制作
根据 savefrom条例
本实例及教程只用于学习交流用,权利归savefrom.net所有

1.准备

i.savfrom.net

一个网站可以免费下载youtube视频

2. 探索并规划获取方式

i.总览

打开https://en.savefrom.net/1-youtube-video-downloader-4/可以看见页面主要就一个from表单输入框和提交按钮组成
video

ii. 获取该网页取到下载url的请求

在chrome或其他浏览器按F12打开开发者控制台,点开Network选项卡
Network
接下来模拟正常的用户流程,在输入框输入一个youtube视频地址,比如这个https://www.youtube.com/watch?v=IjJmTeBSEzU,然后点击Download提交url获取下载url,然后我们可以发现下面已经出现了下载链接。然后我们在network选项卡内可以发现一个特殊的申请saveform.php(如果没发现可以多提交几次,就会发现每次提交都有这个请求或者看html里的form元素的src地址就是savform.php)
saveform
我们就可以确定关于下载链接会出现在这个请求的返回值内。

iii. 在本地获取请求

在本地用IDE的http工具或者python构建出post请求代码,笔者用的是pycharm的httpClient(按2下左Shift,然后输入Tools,然后回车进入菜单,然后点击httpClient下的第一个)
httpclient
把请求里第二个headers的内容全部复制然后格式化然后构建post请求
headers
然后把下面的formdata的参数也复制进来,记得和headers换行
formdata

POST https://en.savefrom.net/savefrom.php
cache-Control: no-cache
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en;q=0.8
content-type: application/x-www-form-urlencoded
cookie: lang=en; country=CN; uid=fd94a82a406a8dd4; sfHelperDist=72; reference=14; clickads-e2=90; poropellerAdsPush-e=63; promoBlock=64; helperWidget=92; helperBanner=42; framelessHdConverter=68; inpagePush2=68; popupInOutput=9; _ga=GA1.2.799702638.1610248969; _gid=GA1.2.628904587.1610248969; PHPSESSID=4aojkm49oj7h3de65pekqii337; x-requested-with=; PHPSESSUD=122fb7e10af37e23d99e896fd188744f; _gat_helperWidget=1; _gat_inpagePush2=1
origin: https://en.savefrom.net
pragma: no-cache
referer: https://en.savefrom.net/1-youtube-video-downloader-4/
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
sec-ch-ua-mobile: ?0
sec-fetch-dest: iframe
sec-fetch-mode: navigate
sec-fetch-site: same-origin
sec-fetch-user: ?1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36

sf_url=https://www.youtube.com/watch?v=YPvtz1lHRiw&sf_submit=&new=1&lang=en&app=&country=cn&os=Windows&browser=Chrome

如果配置没错,这个时候你运行后会显示出和在浏览器里一样的结果,然后可以发现参数里的sf_url就是需要下载的youtube地址

iv.解析请求结果

把取到的结果保存在本地然后在浏览器打开保存的本地文件,然后弹出一个弹窗后会发现这个页面什么都没有,但其实如果你在console输入window会发现有很多变量和函数,其中就有我们需要的加密后的url和解密函数
window
经过弹窗提示我们可以发现是这个html的js发现了我们不在他的网站上然后删除了显示下载url的代码所以我们才什么都看不到,打开source选项卡或者用IDE打开源代码查看
sources
扫一下这个源代码,我们可以看见一些加密解密函数,被加密的乱码信息,哈希对照表,和一些奇怪的赋值操作,如图
code
我们可以看见,js部分由一个大闭包组成

  • 第3行到第9行是声明函数
  • 第10行是给$b赋值this也就是window
  • 第11到第13行是赋值3个变量
  • 第14,15行是对变量执行一些操作,这个也是我们重点关注的,毕竟执行加密,解密方法只可能在这

第14,15行格式化后如下

this.$d = $d;
this.$a = $a;
this.$e = $e;
this.$k = $k;
this.$c = $c;
$c = _q($c);
$c = _b($c);
_m($b, $c);

去掉前5行没有意义的变量互换值,加密解密函数存在的范围就在最后3行

$c = _q($c);
$c = _b($c);
_m($b, $c);

然后开始一个个试
先在console里输入第一行
console
然后就出现了明文,所以我们就确定这个是我们需要的解密函数
接下来输入第二行
console2
发现出现了和上面一模一样的明文,所以这个也是解密函数
接下来输入第三行
发现这个执行了那段代码,这个应该是执行函数,所以我们现在就找到了解密函数位于js部分的最后几行

v.解析解密后的结果

console里用解密函数解密出的结果复制出来会得到一个JavaScript脚本

(function(){
function del(){while(document.body.firstChild){document.body.removeChild(document.body.firstChild);}};
if(window.location.hostname.search(/(?:^|\.|@)(savefrom\.net|sfrom\.net|savefrom\.com\.au|51\.159\.57\.158|(?:fe\d*|test)-front\.sf-apps\.com)$/i) == -1)
{
alert('Please go to http://savefrom.net/ to get direct links');
del();
return;
}

function showResult(){
var success = false;
try
{
var d = window.parent.document;
if(d && d.getElementById)
{
var e = d.getElementById('sf_result');
if(e && e.innerHTML)
{
window.parent.sf.finishRequest(true);;
e.innerHTML = '';
window.parent.sf.videoResult.show({"id":"YPvtz1lHRiw","cipher":false,"meta":{"title":"The Most Genius Plays in Football","source":"https:\/\/www.youtube.com\/watch?v=YPvtz1lHRiw","duration":"7:03","tags":"lionel messi,football tiki taka,football highlight,football team plays,kevin de bruyne,mesut \u00f6zil,paul pogba,cristiano ronaldo,adama traore,luka modric,toni kroos,andres iniesta,kylian mbappe,neymar jr,zlatan ibrahimovic,franck ribery,paulo dybala,philippe coutinho,robert lewandowski,barcelona,real madrid,liverpool,manchester city"},"thumb":"https:\/\/i.ytimg.com\/vi\/YPvtz1lHRiw\/hqdefault.jpg","itags":["18","22","133","134","135","136","137","140","160","242","243","244","247","248","249","250","251","278"],"video_quality":["1080","720","480","360","240","144"],"url":[{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=18&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&gir=yes&clen=35202709&ratebypass=yes&dur=423.137&lmt=1603720879883261&mt=1610277156&fvip=4&c=WEB&txp=5531422&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgN_iBsjO6zSiW9dcOYYExfjeVuy1YCXtWcKT0cz2kMcECIQCMre2rssbqkWdohk9K4Ej8B8r9gARVc0-kAHVqhV1teQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","name":"MP4","subname":"360","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=18&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&gir=yes&clen=35202709&ratebypass=yes&dur=423.137&lmt=1603720879883261&mt=1610277156&fvip=4&c=WEB&txp=5531422&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgN_iBsjO6zSiW9dcOYYExfjeVuy1YCXtWcKT0cz2kMcECIQCMre2rssbqkWdohk9K4Ej8B8r9gARVc0-kAHVqhV1teQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"mp4","ext":"mp4","downloadable":false,"quality":"360","audio":false,"no_audio":false,"itag":"18","filesize":35202709,"attr":{"title":"video format: 360","class":""},"info_token":"0c41b67f74ccb97e50f2e64fe5f4625a"},{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=22&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&ratebypass=yes&dur=423.137&lmt=1603723118280830&mt=1610277156&fvip=4&c=WEB&txp=5535432&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgWg_KEKuXDFH-OcphoOxU2tjE9B0WnidilKpa-vtbmigCIQD-eJlhFHZMi1VzRYI_jh0RZaMiLkkXYWnwGM2TJp0bLA%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D&title=The%20Most%20Genius%20Plays%20in%20Football","name":"MP4","subname":"720","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=22&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&ratebypass=yes&dur=423.137&lmt=1603723118280830&mt=1610277156&fvip=4&c=WEB&txp=5535432&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgWg_KEKuXDFH-OcphoOxU2tjE9B0WnidilKpa-vtbmigCIQD-eJlhFHZMi1VzRYI_jh0RZaMiLkkXYWnwGM2TJp0bLA%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"mp4","ext":"mp4","downloadable":true,"quality":"720","audio":false,"no_audio":false,"itag":"22","attr":{"title":"video format: 720","class":""},"info_token":"674fa89ee246148990b3f36e750a5d32"},{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=137&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=177708494&dur=422.999&lmt=1603723164407782&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhANMFMA1ypwGQjiGT_DlqlYwfqcqu1IlKZGoCrdIqWmzHAiAyOJJHIzUBcMQ6WBeEnepV4UlD3TY6rGSNKBVSVhbpIQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","name":"MP4","subname":"1080","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=137&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=177708494&dur=422.999&lmt=1603723164407782&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhANMFMA1ypwGQjiGT_DlqlYwfqcqu1IlKZGoCrdIqWmzHAiAyOJJHIzUBcMQ6WBeEnepV4UlD3TY6rGSNKBVSVhbpIQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"mp4 dash","ext":"mp4","downloadable":false,"quality":"1080","audio":false,"no_audio":true,"itag":"137","filesize":177708494,"attr":{"title":"video format: 1080 (without audio)","class":"no-audio"},"info_token":"6816215e43020185bb429874c33f1f73"},{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=248&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fwebm&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=117037047&dur=422.999&lmt=1603725149388225&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRgIhANFyBfTGjhDMawSTzN1NjLLYLztYc8e6w3lXVXY5ldbxAiEA-UBT6Yb33Sr0Yhan0x9xlf1xVWFH-wu_y2HsgWkiuXo%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","name":"WEBM","subname":"1080","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=248&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fwebm&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=117037047&dur=422.999&lmt=1603725149388225&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRgIhANFyBfTGjhDMawSTzN1NjLLYLztYc8e6w3lXVXY5ldbxAiEA-UBT6Yb33Sr0Yhan0x9xlf1xVWFH-wu_y2HsgWkiuXo%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"webm dash","ext":"webm","downloadable":false,"quality":"1080","audio":false,"no_audio":true,"itag":"248","filesize":117037047,"attr":{"title":"video format: 1080 (without audio)","class":"no-audio"},"info_token":"43e803aa40437c9930975615121cc2c5"}],"hosting":101,"srv":"s30","sd":null,"hd":null});;
window.parent.sf.enableElement('sf_submit', true);
success=true;
}
}
}
catch(err){}

del();
if(!success)
{
var alt = '';
if(alt) alert(alt);
}
}

try {
if (typeof navigator.permissions != 'undefined') {
navigator.permissions.query({name:'notifications'}).then(function(permissionStatus) {
if(Notification.permission === 'denied' && permissionStatus.state === 'prompt') {
} else {
showResult();
}
});
} else {
showResult();
}
}
catch (err) {
showResult();
}
})();

我们可以发现里面有个del()删除函数,这就是为什么我们看不到这一段代码在执行后
因为被window.location.hostname.search(/(?:^|\.|@)(savefrom\.net|sfrom\.net|savefrom\.com\.au|51\.159\.57\.158|(?:fe\d*|test)-front\.sf-apps\.com)$/i) == -1检查出不是在他的网站上跑的所以就执行弹窗和del()把解密后的东西删除了
仔细检查这段代码可以发现这一行

window.parent.sf.videoResult.show({"id":"YPvtz1lHRiw","cipher":false,"meta":{"title":"The Most Genius Plays in Football","source":"https:\/\/www.youtube.com\/watch?v=YPvtz1lHRiw","duration":"7:03","tags":"lionel messi,football tiki taka,football highlight,football team plays,kevin de bruyne,mesut \u00f6zil,paul pogba,cristiano ronaldo,adama traore,luka modric,toni kroos,andres iniesta,kylian mbappe,neymar jr,zlatan ibrahimovic,franck ribery,paulo dybala,philippe coutinho,robert lewandowski,barcelona,real madrid,liverpool,manchester city"},"thumb":"https:\/\/i.ytimg.com\/vi\/YPvtz1lHRiw\/hqdefault.jpg","itags":["18","22","133","134","135","136","137","140","160","242","243","244","247","248","249","250","251","278"],"video_quality":["1080","720","480","360","240","144"],"url":[{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=18&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&gir=yes&clen=35202709&ratebypass=yes&dur=423.137&lmt=1603720879883261&mt=1610277156&fvip=4&c=WEB&txp=5531422&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgN_iBsjO6zSiW9dcOYYExfjeVuy1YCXtWcKT0cz2kMcECIQCMre2rssbqkWdohk9K4Ej8B8r9gARVc0-kAHVqhV1teQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","name":"MP4","subname":"360","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=18&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&gir=yes&clen=35202709&ratebypass=yes&dur=423.137&lmt=1603720879883261&mt=1610277156&fvip=4&c=WEB&txp=5531422&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgN_iBsjO6zSiW9dcOYYExfjeVuy1YCXtWcKT0cz2kMcECIQCMre2rssbqkWdohk9K4Ej8B8r9gARVc0-kAHVqhV1teQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"mp4","ext":"mp4","downloadable":false,"quality":"360","audio":false,"no_audio":false,"itag":"18","filesize":35202709,"attr":{"title":"video format: 360","class":""},"info_token":"0c41b67f74ccb97e50f2e64fe5f4625a"},{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=22&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&ratebypass=yes&dur=423.137&lmt=1603723118280830&mt=1610277156&fvip=4&c=WEB&txp=5535432&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgWg_KEKuXDFH-OcphoOxU2tjE9B0WnidilKpa-vtbmigCIQD-eJlhFHZMi1VzRYI_jh0RZaMiLkkXYWnwGM2TJp0bLA%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D&title=The%20Most%20Genius%20Plays%20in%20Football","name":"MP4","subname":"720","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=22&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=H2IuXFgrekwcd2kUgtraypIF&ratebypass=yes&dur=423.137&lmt=1603723118280830&mt=1610277156&fvip=4&c=WEB&txp=5535432&n=9544-WEiIwBMq_&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgWg_KEKuXDFH-OcphoOxU2tjE9B0WnidilKpa-vtbmigCIQD-eJlhFHZMi1VzRYI_jh0RZaMiLkkXYWnwGM2TJp0bLA%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"mp4","ext":"mp4","downloadable":true,"quality":"720","audio":false,"no_audio":false,"itag":"22","attr":{"title":"video format: 720","class":""},"info_token":"674fa89ee246148990b3f36e750a5d32"},{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=137&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=177708494&dur=422.999&lmt=1603723164407782&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhANMFMA1ypwGQjiGT_DlqlYwfqcqu1IlKZGoCrdIqWmzHAiAyOJJHIzUBcMQ6WBeEnepV4UlD3TY6rGSNKBVSVhbpIQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","name":"MP4","subname":"1080","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=137&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fmp4&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=177708494&dur=422.999&lmt=1603723164407782&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhANMFMA1ypwGQjiGT_DlqlYwfqcqu1IlKZGoCrdIqWmzHAiAyOJJHIzUBcMQ6WBeEnepV4UlD3TY6rGSNKBVSVhbpIQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"mp4 dash","ext":"mp4","downloadable":false,"quality":"1080","audio":false,"no_audio":true,"itag":"137","filesize":177708494,"attr":{"title":"video format: 1080 (without audio)","class":"no-audio"},"info_token":"6816215e43020185bb429874c33f1f73"},{"url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=248&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fwebm&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=117037047&dur=422.999&lmt=1603725149388225&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRgIhANFyBfTGjhDMawSTzN1NjLLYLztYc8e6w3lXVXY5ldbxAiEA-UBT6Yb33Sr0Yhan0x9xlf1xVWFH-wu_y2HsgWkiuXo%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","name":"WEBM","subname":"1080","info_url":"https:\/\/r4---sn-25glene7.googlevideo.com\/videoplayback?expire=1610299253&ei=FeP6X7qLJvesxN8PteeBaA&ip=154.72.187.26&id=o-AFTOjos_M4e5ymyJ3pa8Zs6JxHQohnez1oxdyfxNGuFl&itag=248&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=youtube&requiressl=yes&mh=nS&mm=31%2C29&mn=sn-25glene7%2Csn-25ge7nzs&ms=au%2Crdu&mv=m&mvi=4&pl=20&initcwndbps=182500&vprv=1&mime=video%2Fwebm&ns=QmTnf2BHCkCLm1nUC3OlqIwF&gir=yes&clen=117037047&dur=422.999&lmt=1603725149388225&mt=1610277156&fvip=4&keepalive=yes&c=WEB&txp=5535432&n=TD5jnO4_esWW9m&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRgIhANFyBfTGjhDMawSTzN1NjLLYLztYc8e6w3lXVXY5ldbxAiEA-UBT6Yb33Sr0Yhan0x9xlf1xVWFH-wu_y2HsgWkiuXo%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALlbQJhLhsKk9tVJdpd0OfXwuiWC26m4cDjWZ8MvxO1MAiEA8EG4gFtyQVAWXRWz_d3DYsCTY4oCiTobXSHKnemPR38%3D","type":"webm dash","ext":"webm","downloadable":false,"quality":"1080","audio":false,"no_audio":true,"itag":"248","filesize":117037047,"attr":{"title":"video format: 1080 (without audio)","class":"no-audio"},"info_token":"43e803aa40437c9930975615121cc2c5"}],"hosting":101,"srv":"s30","sd":null,"hd":null});;

这里面有一长串json文本然后里面就包含我们要的下载链接和其他杂七杂八的东西,在json格式化平台(比如bejson)把这一段json文本输入
json
可以发现meta主要是视频消息,然后url下就是不同分辨率的视频下载链接了

根据 savefrom条例
本实例及教程只用于学习交流用,权利归savefrom.net所有
最后代码+注释大概100行左右,具体代码以github代码为主(可以会在上面修复bug),本文只做具体讲解

3. 设计流程

1. post

根据思路里的第一步,我们首先需要用post方式取到加密后的js字段,笔者使用了requests第三方库来执行,关于爬虫可以参考我之前的文章

i. 先把post中的headers格式化

# set the headers or the website will not return information
# the cookies in here you may need to change
headers = {
"cache-Control": "no-cache",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,"
"*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"content-type": "application/x-www-form-urlencoded",
"cookie": "lang=en; country=CN; uid=fd94a82a406a8dd4; sfHelperDist=72; reference=14; "
"clickads-e2=90; poropellerAdsPush-e=63; promoBlock=64; helperWidget=92; "
"helperBanner=42; framelessHdConverter=68; inpagePush2=68; popupInOutput=9; "
"_ga=GA1.2.799702638.1610248969; _gid=GA1.2.628904587.1610248969; "
"PHPSESSID=030393eb0776d20d0975f99b523a70d4; x-requested-with=; "
"PHPSESSUD=islilfjn5alth33j9j8glj9776; _gat_helperWidget=1; _gat_inpagePush2=1",
"origin": "https://en.savefrom.net",
"pragma": "no-cache",
"referer": "https://en.savefrom.net/1-youtube-video-downloader-4/",
"sec-ch-ua": "\"Google Chrome\";v=\"87\", \"Not;A Brand\";v=\"99\",\"Chromium\";v=\"87\"",
"sec-ch-ua-mobile": "?0",
"sec-fetch-dest": "iframe",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/87.0.4280.88 Safari/537.36"}

其中cookie部分可能要改,然后最好以你们浏览器上的为主,具体每个参数的含义不是本文范围,可以自行去搜索引擎搜

ii.然后把参数也格式化

# set the parameter, we can get from chrome
kv = {"sf_url": url,
"sf_submit": "",
"new": "1",
"lang": "en",
"app": "",
"country": "cn",
"os": "Windows",
"browser": "Chrome"}

其中sf_url字段是我们要下载的youtube视频的url,其他参数都不变

iii. 最后再执行requests库的post请求

# do the POST request
r = requests.post(url="https://en.savefrom.net/savefrom.php", headers=headers,
data=kv)
r.raise_for_status()

注意是data=kv

iv. 封装成一个函数

import requests

def gethtml(url):
# set the headers or the website will not return information
# the cookies in here you may need to change
headers = {
"cache-Control": "no-cache",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,"
"*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"content-type": "application/x-www-form-urlencoded",
"cookie": "lang=en; country=CN; uid=fd94a82a406a8dd4; sfHelperDist=72; reference=14; "
"clickads-e2=90; poropellerAdsPush-e=63; promoBlock=64; helperWidget=92; "
"helperBanner=42; framelessHdConverter=68; inpagePush2=68; popupInOutput=9; "
"_ga=GA1.2.799702638.1610248969; _gid=GA1.2.628904587.1610248969; "
"PHPSESSID=030393eb0776d20d0975f99b523a70d4; x-requested-with=; "
"PHPSESSUD=islilfjn5alth33j9j8glj9776; _gat_helperWidget=1; _gat_inpagePush2=1",
"origin": "https://en.savefrom.net",
"pragma": "no-cache",
"referer": "https://en.savefrom.net/1-youtube-video-downloader-4/",
"sec-ch-ua": "\"Google Chrome\";v=\"87\", \"Not;A Brand\";v=\"99\",\"Chromium\";v=\"87\"",
"sec-ch-ua-mobile": "?0",
"sec-fetch-dest": "iframe",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/87.0.4280.88 Safari/537.36"}
# set the parameter, we can get from chrome
kv = {"sf_url": url,
"sf_submit": "",
"new": "1",
"lang": "en",
"app": "",
"country": "cn",
"os": "Windows",
"browser": "Chrome"}
# do the POST request
r = requests.post(url="https://en.savefrom.net/savefrom.php", headers=headers,
data=kv)
r.raise_for_status()
# get the result
return r.text

2. 调用解密函数

i. 分析

这其中的难点在于在python里执行javascript代码,而网上的解决方法有PyV8等,本文选用execjs。在思路部分我们可以发现js部分的最后几行是解密函数,所以我们只需要在execjs中先执行一遍全部,然后再单独执行解密函数就好了

ii. 先取出js部分

# target(youtube address) url
url = "https://www.youtube.com/watch?v=YPvtz1lHRiw"
# get the target text
reo = gethtml(url)
# Remove the code from the head and tail (we need the javascript part, information store with encryption in js part)
reo = reo.split("<script type=\"text/javascript\">")[1].split("</script>")[0]

这里其实可以用正则,不过由于笔者正则表达式还不太熟练就直接用split

iii. 取第一个解密函数作为我们用的解密函数

当你多取几次不同视频的结果,你就会发现每次的解密函数都不一样,不过位置都是还是在固定行数

# split each line(help us find the decrypt function in last few line)
reA = reo.split("\n")
# get the depcrypt function
name = reA[len(reA) - 3].split(";")[0] + ";"

所以name就是我们的解密函数了(变量名没取太好hhh)

iv. 用execjs执行

# use execjs to execute the js code, and the cwd is the result of `npm root -g`(the path of npm in your computer)
ct = execjs.compile(reo)
# do the decryption
text = ct.eval(name.split("=")[1].replace(";", ""))

其中只取=后面的和去掉分号是指指执行这个函数而不用赋值,当然先执行赋值+解密然后取值也不是不可以
但是我们可以发现马上就报错了(要是有这么简单就好了)

1. this也就是window变量不存在

如果没记错是报错this或者$b,笔者尝试把全部this去掉或者把全部框在一个class里面(这样子this就变成那个class了)不过都没有成功,然后发现在npm下有个jsdom可以在execjs里模拟window变量(其实应该有更好方法的),所以我们需要下载npm和里面的jsdom,然后改写以上代码

addition = """
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
window = dom.window;
document = window.document;
XMLHttpRequest = window.XMLHttpRequest;
"""
# use execjs to execute the js code, and the cwd is the result of `npm root -g`(the path of npm in your computer)
ct = execjs.compile(addition + reo, cwd=r'C:\Users\xxx\AppData\Roaming\npm\node_modules')

其中

  • cwd字段是npm root -g的结果,也就是npm的modules路径
  • addition是用来模拟window
    但是我们又可以发现下一个错误

    2. alert不存在

    这个错误是因为在execjs下执行alert函数是没有意义的,因为我们没有浏览器让他弹窗,且原本alert函数的定义是来源window而我们自定义了window,所以我们要在代码前重写覆盖alert函数(相当于定义一个alert)
    # override the alert function, because in the code there has one place using
    # and we cannot do the alerting in execjs(it is meaningless) however, if we donnot override, the code will raise a error
    reo = reo.replace("(function(){", "(function(){\nthis.alert=function(){};")

    v. 整合代码

    # target(youtube address) url
    url = "https://www.youtube.com/watch?v=YPvtz1lHRiw"
    # get the target text
    reo = gethtml(url)
    # Remove the code from the head and tail (we need the javascript part, information store with encryption in js part)
    reo = reo.split("<script type=\"text/javascript\">")[1].split("</script>")[0]
    # override the alert function, because in the code there has one place using
    # and we cannot do the alerting in execjs(it is meaningless) however, if we donnot override, the code will raise a error
    reo = reo.replace("(function(){", "(function(){\nthis.alert=function(){};")
    # split each line(help us find the decrypt function in last few line)
    reA = reo.split("\n")
    # get the depcrypt function
    name = reA[len(reA) - 3].split(";")[0] + ";"
    # add jsdom into the execjs because the code will use(maybe there is a solution without jsdom, but i have no idea)
    addition = """
    const jsdom = require("jsdom");
    const { JSDOM } = jsdom;
    const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
    window = dom.window;
    document = window.document;
    XMLHttpRequest = window.XMLHttpRequest;
    """
    # use execjs to execute the js code, and the cwd is the result of `npm root -g`(the path of npm in your computer)
    ct = execjs.compile(addition + reo, cwd=r'C:\Users\19308\AppData\Roaming\npm\node_modules')
    # do the decryption
    text = ct.eval(name.split("=")[1].replace(";", ""))

    3. 分析解密结果

    i. 取关键json

    运行完上面的部分,解密结果就存在text里了,而我们在思路中可以发现,真正对我们重要的就是存在window.parent.sf.videoResult.show()里的json,所以用正则表达式取这一部分的json
    # get the result in json
    result = re.search('show\((.*?)\);;', text, re.I | re.M).group(0).replace("show(", "").replace(");;", "")

    ii. 格式化json

    python可以格式化json的库有很多,这里笔者用了json库(记得import)
    # use `json` to load json
    j = json.loads(result)

    iii. 取下载地址

    接下来就到了最后一步,根据思路里和json格式化工具我们可以发现j["url"][num]["url"]就是下载链接,而num是我们要的视频格式(不同分辨率和类型)
    # the selection of video(in this case, num=1 mean the video is
    # - 360p known from j["url"][num]["quality"]
    # - MP4 known from j["url"][num]["type"]
    # - audio known from j["url"][num]["audio"]
    num = 1
    downurl = j["url"][num]["url"]
    # do some download
    # thanks :)
    # - EOF -

    4. 全部代码

# -*- coding: utf-8 -*-
# @Time: 2021/1/10
# @Author: Eritque arcus
# @File: Youtube.py
# @License: MIT
# @Environment:
# - windows 10
# - python 3.6.2
# @Dependence:
# - jsdom in npm(windows also can use)
# - requests, execjs, re, json in python
import requests
import execjs
import re
import json


def gethtml(url):
# set the headers or the website will not return information
# the cookies in here you may need to change
headers = {
"cache-Control": "no-cache",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,"
"*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"content-type": "application/x-www-form-urlencoded",
"cookie": "lang=en; country=CN; uid=fd94a82a406a8dd4; sfHelperDist=72; reference=14; "
"clickads-e2=90; poropellerAdsPush-e=63; promoBlock=64; helperWidget=92; "
"helperBanner=42; framelessHdConverter=68; inpagePush2=68; popupInOutput=9; "
"_ga=GA1.2.799702638.1610248969; _gid=GA1.2.628904587.1610248969; "
"PHPSESSID=030393eb0776d20d0975f99b523a70d4; x-requested-with=; "
"PHPSESSUD=islilfjn5alth33j9j8glj9776; _gat_helperWidget=1; _gat_inpagePush2=1",
"origin": "https://en.savefrom.net",
"pragma": "no-cache",
"referer": "https://en.savefrom.net/1-youtube-video-downloader-4/",
"sec-ch-ua": "\"Google Chrome\";v=\"87\", \"Not;A Brand\";v=\"99\",\"Chromium\";v=\"87\"",
"sec-ch-ua-mobile": "?0",
"sec-fetch-dest": "iframe",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/87.0.4280.88 Safari/537.36"}
# set the parameter, we can get from chrome
kv = {"sf_url": url,
"sf_submit": "",
"new": "1",
"lang": "en",
"app": "",
"country": "cn",
"os": "Windows",
"browser": "Chrome"}
# do the POST request
r = requests.post(url="https://en.savefrom.net/savefrom.php", headers=headers,
data=kv)
r.raise_for_status()
# get the result
return r.text


if __name__ == '__main__':
# target(youtube address) url
url = "https://www.youtube.com/watch?v=YPvtz1lHRiw"
# get the target text
reo = gethtml(url)
# Remove the code from the head and tail (we need the javascript part, information store with encryption in js part)
reo = reo.split("<script type=\"text/javascript\">")[1].split("</script>")[0]
# override the alert function, because in the code there has one place using
# and we cannot do the alerting in execjs(it is meaningless) however, if we donnot override, the code will raise a error
reo = reo.replace("(function(){", "(function(){\nthis.alert=function(){};")
# split each line(help us find the decrypt function in last few line)
reA = reo.split("\n")
# get the depcrypt function
name = reA[len(reA) - 3].split(";")[0] + ";"
# add jsdom into the execjs because the code will use(maybe there is a solution without jsdom, but i have no idea)
addition = """
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
window = dom.window;
document = window.document;
XMLHttpRequest = window.XMLHttpRequest;
"""
# use execjs to execute the js code, and the cwd is the result of `npm root -g`(the path of npm in your computer)
ct = execjs.compile(addition + reo, cwd=r'C:\Users\19308\AppData\Roaming\npm\node_modules')
# do the decryption
text = ct.eval(name.split("=")[1].replace(";", ""))
# get the result in json
result = re.search('show\((.*?)\);;', text, re.I | re.M).group(0).replace("show(", "").replace(");;", "")
# use `json` to load json
j = json.loads(result)
# the selection of video(in this case, num=1 mean the video is
# - 360p known from j["url"][num]["quality"]
# - MP4 known from j["url"][num]["type"]
# - audio known from j["url"][num]["audio"]
num = 1
downurl = j["url"][num]["url"]
# do some download
# thanks :)
# - EOF -

  • 总计102行
  • 开发环境
    # @Environment:
    # - windows 10
    # - python 3.6.2
  • 依赖
    # @Dependence:
    # - jsdom in npm(windows also can use)
    # - requests, execjs, re, json in python
    END