|
前言:
她说要减肥,要下载广场舞在电视上放来学,本以为很简单的事情,网上随便搜索下载就是,于是随口答应。。。
结果,辛苦搜索半天基本都没什么能下载的,逼我动用神器:AARDIO
先找到一个播放网址:
http://www.boosj.com/drama_43355_29.html
页面上没有下载按钮,于是看了下网页源码,也没发现什么直接的视频地址,只找到了这个一段视频信息配置
既然没有直接写在网页源码里面,那就F12分析看看,在等待了几十秒的广告后,开始播放了,发现播放的不是一个完整的MP4或FLV等视频文件,而是多个TS文件
根据经验,这个的视频文件一般有个分段视频配置文件,于是往上找,找到了这样一个网址:http://v10.hls.boosj.com/v/2016-12/29/2016-1229SVNE123110511031020818.mp4/index.m3u8?t=tKZEMKlur-P-JdRlzymCPg&m=1528634372
果然这里面保存的就是完整的视频地址
在来找找这个配置文件的网址是哪里来的,继续往上翻,找到了这个一个网址:
http://gslb.boosj.com/f_hls/?_id=6888231&t=99155818ca3a229879eff66e9f4955a8
挺顺利啊,继续找这个网址,咦,貌似不需要继续找了,网页源码里面貌似就有这个id,在第一张截图里面的 vid 参数 和这个 _id参数一致,模拟GET这个视频播放网址,提取出 vid参数,直接就能构建出这个 http://gslb.boosj.com/f_hls/?_id=视频编号&t=99155818ca3a229879eff66e9f4955a8
OK,祭出神器【aardio】,开工测试一下上面这些流程
- import console;
- import web.json
- import inet.http
- http = inet.http()
- html = http.get( "http://www.boosj.com/drama_43355_29.html" )
- 视频编号 = string.match( html, '<@vid:"@>([0-9]+)' )
- hls = "http://gslb.boosj.com/f_hls/?_id="+视频编号+"&t=99155818ca3a229879eff66e9f4955a8"
- html = http.get( hls )
- json = web.json.tryParse( html )
- m3u8 = json.url ++ "?" ++ json.t
- html = http.get( m3u8 )
- for ts in ..string.gmatch( html, "(http.*?\.ts)" ) {
- console.log( ts )
- }
- console.pause(true);
复制代码 运行结果
视频地址全出来了,继续测试其他的播放页面看看,把代码里面的网址换成:http://www.boosj.com/drama_43355_36.html
结果报错,json.t 不存在,打印出来瞧瞧:
看来网址 "http://gslb.boosj.com/f_hls/?_id="+视频编号+"&t=99155818ca3a229879eff66e9f4955a8" 里面的 参数t 不是固定的
于是删除t 随机t 各种尝试,始终报错,看来不能直接拼接 "http://gslb.boosj.com/f_hls/?_id="+视频编号+"&t=99155818ca3a229879eff66e9f4955a8"这个网址
那就继续分析哪里生成的这个 t 参数,结果分析了半天,F12里面看了个遍,都没找到哪里有生成这个 t 参数,也没找到有 f_hls 关键字的内容,没道理啊
继续在调试器里面东看西看,冥思苦想,看到个v_4.3.swf播放器文件,难道是这个v_4.3.swf文件生成的t参数,下载下来分析下:
http://static.boosj.com/v/swf/v_4.3.swf
找了个自己的百宝箱,不知道我的FLASH反编译工具跑哪里去了,只有一个 URL Action Editor6.0 ,就你了,先看看吧
哈哈,果然藏在这里,看你往哪儿跑,赶紧下载个反编译工具JPEXS Free Flash Decompiler,找到这个怎么生成的
可以看出这个 t = md5(视频编号+01136c5948d353b1bg2) 或者 t = md5(视频编号+01136c5948d353b1)
从前面步骤的网址可以看出我们需要的t应该是用 版本HLS的 01136c5948d353b1bg2 但是还是测试一下比较保险
在前面有这个一个地址:http://gslb.boosj.com/f_hls/?_id ... 29879eff66e9f4955a8
我们来测试看看,结果确实是一致的,这里使用这个固定的01136c5948d353b1bg2
现在问题解决,再测试几个网址,也都正常了,可以愉快的下载了
不过控制台窗口确实不好操作,优化一下搞个稍微方便点的小工具吧
源码:(代码有点乱,将就看,也就是个思路)
- import win.ui;
- /*DSG{{*/
- winform = win.form(text="广场舞下载";right=807;bottom=639)
- winform.add(
- button={cls="button";text="抓呀";left=717;top=11;right=794;bottom=47;z=4};
- listview={cls="listview";left=14;top=57;right=795;bottom=455;edge=1;fullRow=1;gridLines=1;hscroll=1;vscroll=1;z=3};
- log={cls="edit";left=14;top=469;right=795;bottom=630;bgcolor=0;color=65280;edge=1;multiline=1;vscroll=1;z=5};
- static={cls="static";text="播放网址";left=8;top=19;right=64;bottom=41;align="right";transparent=1;z=2};
- url={cls="edit";left=69;top=18;right=705;bottom=40;edge=1;multiline=1;z=1}
- )
- /*}}*/
- winform.listview.insertColumn("名称",200);
- winform.listview.insertColumn("文件数",100);
- winform.listview.insertColumn("已下载",100);
- winform.listview.insertColumn("状态",100);
- winform.listview.adjust = function(cx,cy){
- winform.listview.fillParent(1);
- }
- import process
- import fsys
- import fsys.dlg
- import crypt
- import mouse;
- import win.util.tray;
- import win.ui.menu;
- import crypt
- import thread.manage
- manage = thread.manage()
- import thread.command
- cmd = thread.command()
- thread.set("cmdhwnd", cmd.hwnd)
- thread.set("listviewHwnd",winform.listview.hwnd)
- 日志 = function(...){
- winform.log.print(...)
- }
- cmd.subscribe("日志",日志)
- 解析 = function(网址){
- import win
- import crypt
- import web.json
- import fsys
- import thread.command;
- cmd = thread.command.bind(thread.get("cmdhwnd"));
- import win.ui.ctrl.listview
- listview = win.ui.ctrl.listview()
- listview.hwnd = thread.get("listviewHwnd")
- import inet.http
- import inet.httpFile
- http = inet.http("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0")
-
- cmd.post("日志", "解析网址", 网址)
- //第一步 获取视频基本信息
- var html = http.get(网址)
- if(!html or !#html){
- cmd.post("日志", "打开网址失败")
- return ;
- }
-
- var 视频编号 = string.match(html,'<@vid:"@>([0-9]+)')
- var 视频名称 = string.match(html,"videoName\s*?\:\s*?\'(.*?)\'")
- 视频名称 = string.replace(视频名称,"\s+","_")
- if(!视频编号){
- cmd.post("日志", "视频编号获取失败")
- return ;
- }
- if(!视频名称){
- 视频名称 = crypt.md5(网址)
- }
- cmd.post("日志", "视频编号",视频编号)
- cmd.post("日志", "视频名称",视频名称)
-
-
- //第二步 找到分段视频配置文件m3u8
- var t = crypt.md5(视频编号++"01136c5948d353b1bg2",false);
- var url2 = "http://gslb.boosj.com/f_hls/?_id="++视频编号++"&t="++t;
- var html2 = http.get(url2)
- if(!html2 or !#html2){
- cmd.post("日志", "获取分段视频配置文件失败",url2)
- return ;
- }
- var m3u8 = web.json.tryParse(html2)
- if(!m3u8 or m3u8.error!=200 or !m3u8.url or !m3u8.t){
- cmd.post("日志", "m3u8错误",html2)
- return ;
- }
-
- //第三步 提取分段视频网址
- var url3 = m3u8.url ++ "?" ++ m3u8.t
- var html3 = http.get(url3)
- if(!html3 or !#html3){
- cmd.post("日志", "分段视频网址获取失败",url3)
- return ;
- }
- var 下载地址 = {}
- for 视频地址 in ..string.gmatch( html3,"(http.*?\.ts)") {
- table.push(下载地址, 视频地址)
- }
-
- var 已完成 = 0;
- var 文件数 = #下载地址;
- if(!文件数){
- cmd.post("日志", "未获取到分段视频下载地址",html3)
- return ;
- }
- cmd.post("日志", "分段视频个数",文件数)
-
- //第四步 开始下载
- down = function(网址,目录){
- var dw = inet.httpFile(网址,目录)
- if( dw.test() ){
- return true ;
- }
- var ok,err,fileSize = dw.download()
- return ok,err,fileSize;
- }
- var id = listview.addItem( 视频名称 );
- listview.setItemText( 文件数, id, 2 );
-
- var x = 10
- for(i=1;#tostring(文件数);1){
- x *= 10
- }
-
- var GUID = crypt.md5(视频名称)
- var 下载目录 = io.fullpath(""++GUID)
- for(k,v in 下载地址){
- var ok,err = down(v, 下载目录++""++(x+k)++".ts")
- if(ok){
- 已完成++
- listview.setItemText( 已完成, id, 3 );
- }
- }
- if(已完成==文件数){
- listview.setItemText( "下载完成", id, 4 );
- }else {
- 已完成 = 0
- var ok,err = down(v, "/"+视频名称+"/")
- if(ok){
- 已完成++
- listview.setItemText( 已完成, id, 3 );
- }
- }
-
- if(已完成==文件数){
- listview.setItemText( "下载完成", id, 4 );
- var 完成文件 = io.fullpath(下载目录++"/"++视频名称++".ts")
- if(!io.exist("/视频/")){
- fsys.createDir("/视频/")
- }
- var 合并命令 = "copy/b "++GUID+'\\'+"*.ts "++'视频\\'+视频名称++".ts"
- 合并命令 = string.fromto(合并命令,65001,0)
- execute(合并命令)
- cmd.post("日志", "任务结束","/视频/"+视频名称+".ts")
- }else {
- listview.setItemText( "部分完成", id, 4 );
- cmd.post("日志", "任务结束","下载未全部完成,暂未合并")
- }
- return 1;
- }
- winform.button.oncommand = function(id,event){
- var 网址 = string.trim( winform.url.text )
- if(!#网址){
- winform.msgboxErr("请输入视频网址")
- return ;
- }
- if(string.slice(网址,0,4)!="http"){
- 网址 = "http://"++网址
- }
- manage.create(解析, 网址)
- }
- zpop = win.ui.popmenu(winform);
- zpop.add('打开视频目录',function(id){
- var 视频名称 = winform.listview.getItemText(winform.listview.selIndex,1)
- if(#视频名称){
- if(!io.exist("/视频/")){
- fsys.createDir("/视频/")
- process.explore("/视频/"++视频名称++".ts")
- }else {
- process.exploreSelect("/视频/"++视频名称++".ts")
- }
- }
- });
- zpop.add()
- zpop.add('打开下载目录',function(id){
- var 视频名称 = winform.listview.getItemText(winform.listview.selIndex,1)
- if(#视频名称){
- var GUID = crypt.md5(视频名称)
- if(!io.exist("/"+GUID+"/")){
- fsys.createDir("/"+GUID+"/")
- }
- process.explore("/"+GUID)
- }
- });
- zpop.add()
- zpop.add('播放',function(id){
- var 视频名称 = winform.listview.getItemText(winform.listview.selIndex,1)
- if(#视频名称){
- if(!io.exist("/视频/"++视频名称++".ts")){
- winform.msgboxErr("视频文件不存在")
- return ;
- }
- process.execute("/视频/"++视频名称++".ts")
- }
- });
- zpop.add()
- zpop.add('合并下载目录ts文件',function(id){
- var 视频名称 = winform.listview.getItemText(winform.listview.selIndex,1)
- if(#视频名称){
- var GUID = crypt.md5(视频名称)
- if(!io.exist("/"+GUID+"/")){
- winform.msgboxErr("分段视频目录不存在")
- return ;
- }
- var 合并命令 = "copy/b "++GUID+'\\'+"*.ts "++'视频\\'+视频名称++".ts"
- 合并命令 = string.fromto(合并命令,65001,0)
- execute(合并命令)
-
- var 视频文件 = "/视频/"++视频名称++".ts"
- if(!io.exist(视频文件)){
- 日志("合并失败",GUID++"\*.ts")
- winform.msgboxErr("请检查分段TS文件是否存在","合并失败")
- return ;
- }
- 日志("合并成功",视频文件)
-
- process.exploreSelect(视频文件)
- }else {
- var 合并目录 = fsys.dlg.opendir(io._exedir,,"选择待合并的TS文件目录","该目录下的所有TS文件将合并成一个文件,请注意TS文件名称排序问题")
- if(合并目录){
- var 视频名称 = crypt.md5(合并目录)
- var 合并命令 = "copy/b "++合并目录+'\\'+"*.ts "++'视频\\'+视频名称++".ts"
- 合并命令 = string.fromto(合并命令,65001,0)
- execute(合并命令)
-
- var 视频文件 = "/视频/"++视频名称++".ts"
- if(!io.exist(视频文件)){
- 日志("合并失败",合并目录++"\*.ts")
- winform.msgboxErr("请检查分段TS文件是否存在","合并失败")
- return ;
- }
- 日志("合并成功",视频文件)
- }
- }
-
- });
- zpop.add()
- zpop.add('移除',function(id){
- winform.listview.delItem( winform.listview.selIndex )
- });
- winform.listview.onnotify = function(id,code,ptr){
- select(code) {
- case 0xFFFFFFFB/*_NM_RCLICK*/ {
- var x,y = mouse.getPos()
- zpop.popup(x,y,true);//弹出菜单
- }
- case 0xFFFFFFFD/*_NM_DBLCLK*/ {
- var 名称 = winform.listview.getItemText(winform.listview.selIndex,1)
- if(#名称){
- if(!io.exist("/视频/")){
- fsys.createDir("/视频/")
- }
- process.exploreSelect("/视频/"++名称++".ts")
- }else {
- process.explore("/视频/")
- }
- }
- }
- }
- winform.onClose = function(hwnd,message,wParam,lParam){
- winform.text = "正在等待关闭";
- manage.quitMessage();
- }
- winform.enableDpiScaling();
- winform.show();
- return win.loopMessage();
复制代码
OK,到此结束!感谢神器AARDIO
|
|