首页
社区
课程
招聘
[原创]杀鸡焉用牛刀,再探好听音乐网
发表于: 2009-6-26 13:53 12942

[原创]杀鸡焉用牛刀,再探好听音乐网

2009-6-26 13:53
12942

看了破船兄的文章【原创】好听音乐网 -- 歌曲下载分析流程及程序的编写,我也来写一个。破船的代码是智慧型的,我来个猛士型的。。

打开好听音乐网的首页,随便点一首歌来听,出现了播放界面,同时音乐也出来了,这时查看源文件,容易发现如下关键代码

           if("undefined"==typeof(UUAuthCode)|| UUAuthCode==null)  UUAuthCode="";
	else UUAuthCode="?"+UUAuthCode;
        [COLOR="red"] urlht = "/21z/0/daodaidq090508/1/8f4257328b24c8d0_5.wma"+[/COLOR]UUAuthCode;
} 
seto();

[COLOR="Red"]var url='http://wma.haoting.com';[/COLOR]

document.writeln("<object id=\"mPlayer\" width=365 height=62");
document.writeln(" classid=\"CLSID:6BF52A52-394A-11D3-B153-00C04F79FAA6\" type=application\/x-oleobject standby=\"Loading Windows Media Player ");
document.writeln("");
document.writeln("components...\">");
document.writeln("             <param name=\"URL\[COLOR="red"]" value='"+url+""+urlht+"'[/COLOR]>");

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (15)
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
3
原文有误,以 /21z/ 做为特征码并不能保证 100%找到下载地址,应该改为用 .wma 做为特征码,应该修改为
[color=#0000D0]for[/color] ([color=#0000D0]UINT[/color] i = 0; i < FileSize.u.LowPart; i++)
		{

			[color=#0000D0]if[/color] ((*lpByte == '.')           [color=#008000]//判断是不是找到了特征代码[/color]
				&& (*(lpByte + 1) == 'w') 
				&& (*(lpByte + 2) == 'm') 
				&& (*(lpByte + 3) == 'a'))
			{
	
				[color=#0000D0]for[/color] (;;)
				{
					
					lpByte -= 1;

					[color=#0000D0]if[/color] (*(lpByte - 1) == '[color=#808080]"')  //退回到左边的引号开始的地方[/color]
					{
						
						[color=#0000D0]for[/color] ([color=#0000D0]UINT[/color] j = 0; ; j++)
						{
							szBuffer[j] = *lpByte;
							
							[color=#0000D0]if[/color] (*(++lpByte) == '[color=#808080]"') //判断是不是已经到尽头了[/color]
							{
								[color=#0000D0]break[/color];
							}

						}

						[color=#0000D0]break[/color];
					}

					
				}

				[color=#0000D0]break[/color];
			}

			lpByte += 1;
		}
]
给大家带来不便,对不起了。。
上传的附件:
2009-6-26 16:13
0
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
4
收益了,嘿嘿。
我没有仔细的看他的JS代码,没有找到这样的代码,不过看了同学你的文章以后,仔细看了一下我的程序,才发现,我的程序也存在问题的。


urlht = "/21z/0/daodaidq090508/1/8f4257328b24c8d0_5.wma"+UUAuthCode;

这里的“UUAuthCode”起到了一定的防止下载的作用。

很佩服LZ的学习精神,共同努力吧,嘿嘿
2009-6-27 11:16
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
5
破船通过分析js的方法能获取很多有用信息,对于我等懒人,本来想拿来改改就用的, 但是在使用过程中还是发现了一个小问题,在分析到一个页面的时候,出现了乱码。。

反正源码已经有了,改改就行了,但是分析js实在是。。俺小菜目前的水平还不会,干脆还是直接读它的源文件吧,通过分析源文件,我们已经知道,真实的下载地址就是 url + urlht urlht 每首歌都不一样,而且url也不是一个固定值,在这里又要对不起大家了,之前我一直认为它是一个固定值,就没有把它分析出来。发现了这个问题,只要修改分析引擎就可以达到我们的要求了,当然也可以分析出歌曲名,歌手,和所属专辑。在这里我只分析了它的下载地址并且封装在了GetHaoTingWMAPath这个函数里,对于我们来说,这个才是最重要的。修改后的代码如下:
[color=#0000D0]BOOL[/color] [color=#0000D0]WINAPI[/color] GetHaoTingWMAPath([color=#0000D0]char[/color] * lpFullPath[color=#008000]/*指向存放下载地址的缓冲区指针*/[/color], [color=#0000D0]LPCTSTR[/color] lpURL[color=#008000]/*播放音乐的页面*/[/color], [color=#0000D0]LPCTSTR[/color] lpFileName[color=#008000]/*存放源文件的临时文件,会在文件关闭后被自动删除*/[/color])
{
  [color=#0000D0]char[/color] szBuffer[256];

  [color=#FF0000]memset[/color](szBuffer, 0, [color=#0000D0]sizeof[/color]([color=#0000D0]char[/color]) * 256);

  [color=#0000D0]if[/color] ([color=#0000D0]S_OK[/color] == [b][color=#000080]URLDownloadToFile[/color][/b]([color=#0000D0]NULL[/color], 
    lpURL, 
    lpFileName,
    0,
    [color=#0000D0]NULL[/color]))
  {
    [color=#0000D0]HANDLE[/color] hFile = [b][color=#000080]CreateFile[/color][/b](lpFileName, 
      GENERIC_READ, 
      FILE_SHARE_READ,
      [color=#0000D0]NULL[/color], OPEN_EXISTING,
      FILE_FLAG_DELETE_ON_CLOSE,
      [color=#0000D0]NULL[/color]);
    
        
    [color=#0000D0]if[/color] (INVALID_HANDLE_VALUE == hFile)
    {
      hFile = [color=#0000D0]NULL[/color];
      [color=#0000D0]return[/color] [color=#0000D0]FALSE[/color];
    }

    LARGE_INTEGER FileSize = {0};

    [b][color=#000080]GetFileSizeEx[/color][/b](hFile, &FileSize);
    
    [color=#0000D0]HANDLE[/color] hMemFile = [b][color=#000080]CreateFileMapping[/color][/b](hFile, [color=#0000D0]NULL[/color], PAGE_READONLY, 0, 0, [color=#0000D0]NULL[/color]);

    [color=#0000D0]if[/color] (INVALID_HANDLE_VALUE == hMemFile)
    {
      hMemFile = [color=#0000D0]NULL[/color];
      [color=#0000D0]return[/color] [color=#0000D0]FALSE[/color];
    }

    [color=#0000D0]LPBYTE[/color] lpByte = ([color=#0000D0]LPBYTE[/color])[b][color=#000080]MapViewOfFile[/color][/b](hMemFile, FILE_MAP_READ, 0, 0, 0);

    [color=#0000D0]for[/color] ([color=#0000D0]UINT[/color] i = 0; i < FileSize.u.LowPart; i++)
    {

      [color=#0000D0]if[/color] ((*lpByte == '.')           [color=#008000]//判断是不是找到了特征代码[/color]
        && (*(lpByte + 1) == 'w') 
        && (*(lpByte + 2) == 'm') 
        && (*(lpByte + 3) == 'a'))
      {
  
        [color=#0000D0]for[/color] (;;)
        {
          
          lpByte -= 1;

          [color=#0000D0]if[/color] (*(lpByte - 1) == '[color=#808080]"')[/color]
          {
            
            [color=#0000D0]for[/color] ([color=#0000D0]UINT[/color] j = 0; ; j++)
            {
              szBuffer[j] = *lpByte;
              
              [color=#0000D0]if[/color] ((*lpByte == 'a')
                && (*(lpByte - 1) == 'm')
                && (*(lpByte - 2) == 'w')
                && (*(lpByte - 3) == '.'))
              {
                [color=#0000D0]break[/color];
              }

              lpByte += 1;

            }

            [color=#0000D0]break[/color];
          }

          
        }
        
        [color=#0000D0]for[/color] ([color=#0000D0]UINT[/color] k = 0; ; k++)
        {
          

          [color=#0000D0]if[/color] ((*lpByte == 'h')
            && (*(lpByte + 1) == 'a')
            && (*(lpByte + 2) == 'o')
            && (*(lpByte + 3) == 't')
            && (*(lpByte + 4) == 'i')
            && (*(lpByte + 5) == 'n')
            && (*(lpByte + 6) == 'g')
            && (*(lpByte + 7) == '.')
            && (*(lpByte + 8) == 'c')
            && (*(lpByte + 9) == 'o')
            && (*(lpByte + 10) == 'm'))
          {
            
            [color=#0000D0]for[/color] (;;)
            {
              lpByte -= 1;

              [color=#0000D0]if[/color] ((*(lpByte - 1) == '\''))
              {
              
                [color=#0000D0]for[/color] ([color=#0000D0]UINT[/color] l = 0; ; l++)
                {
                  lpFullPath[l] = *lpByte;

                  [color=#0000D0]if[/color] ((*lpByte == 'm')
                    && (*(lpByte - 1) == 'o')
                    && (*(lpByte - 2) == 'c')
                    && (*(lpByte - 3) == '.'))
                  {
                    [color=#0000D0]break[/color];
                  }

                  lpByte += 1;
                }

                [color=#0000D0]break[/color];
              }

            }
            
            [color=#0000D0]break[/color];
          }

          lpByte += 1;

        }

        [color=#0000D0]break[/color];
      }

      lpByte += 1;
    }
    
    [b][color=#000080]UnmapViewOfFile[/color][/b](([color=#0000D0]LPVOID[/color])lpByte);

    [b][color=#000080]CloseHandle[/color][/b](hMemFile);
    [b][color=#000080]CloseHandle[/color][/b](hFile);
    
    lstrcatA(lpFullPath, szBuffer);

    [color=#0000D0]return[/color] [color=#0000D0]TRUE[/color];

  }

  [color=#0000D0]return[/color] [color=#0000D0]FALSE[/color];

}

为了方便,我写了个GUI界面来查看效果:

附件里是我写的Demo程序,我试过很多页面,都可以读出来。破船的那个程序有一个缺点,就是分析起来太久了,对于批量分析不太适用。。。

PS:
刚刚编辑完帖子,就发现破船回复了。。我写的这个分析引擎貌似它的防下载没有作用,因为我只读到.wma就不再读了,它后面是什么内容都不用管了...
上传的附件:
2009-6-27 11:33
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
这个用正则表达式很好做的
用脚本比如python等做的话会简单很多
2009-6-27 18:54
0
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
7
很好很强大,嘿嘿。
我的程序写出来就发上来了,问题肯定很多啦,嘿嘿。
一个功能有好多的实现方法,多多交流,相互学习进步吧。

再次感谢cntrump 的指教^
2009-6-27 18:59
0
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
8
至于乱码的问题,我也不清楚问题出在哪里,估计是我获取JS代码的函数没有做判断导致的。
至于慢,是因为我网络环境比较差,我加了Sleep等待。嘿嘿,去掉就可以了。
2009-6-27 19:06
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
9
之前我一直都是实现单首播放时的歌曲路径获取,一直没有实现获取多曲播放页面时的音乐路径,实在不想留下这个遗憾,今天就一鼓作气把它拿下
首先在好听音乐网上选定几首歌曲连续播放,打开播放页面查看源文件,可以发现
var strstr = document.location.search;
var arrtmp = strstr.substr(1,strstr.length).split("&");
var qString = new Array();
for(iii=0;iii < arrtmp.length;iii++){ 
 	var tmpStr2 = arrtmp[iii].split("=");
	if(tmpStr2[0]=="id")	document.write("<script src=\"../musicjs/"+(qString[iii]=tmpStr2[1])+".js\"><\/script>");	
	else break;
}

上面的这段代码意思是把地址中的歌曲ID分离出来,再和../musicjs/这个字符串连接,末尾再加上.js组成一个新的路径,这是一个相对路径,必需把把它转换为绝对的网址我们才能下载这个js文件.从../musicjs/这个字符串我们可以知道,这个目录是相对于网站的根目录的,可以轻易知道完整的URL就是http://www.haoting.com/musicjs/[id].js,从这里我们也可以知道,每个歌曲对应唯一个js,而且是以歌曲ID作为区分的,对于单曲播放也是一样,到底是不是这样?我们可以这样验证,随机输入一个ID做为歌曲的ID来下载相应的js是不是是可以呢?我构造了一个简单的地址http://www.haoting.com/musicjs/1.js,果然弹出来了我想要文件下载对话框。OK,Let's go!打开这个js文件,里面的内容不多
var url_1="/ahn/a/adu/1/1.wma";
var music_1="Andy";
var singer_1="阿杜(A-Du)";
var nclassid_1=2;
var specialid_1="31a616e6816d20fc";
var specialName_1="天黑";

我们需要的信息全部在里面了,有wma的地址(相对地址),歌曲名字,歌手名字,所属专辑
有了wma的相对地址,怎么获取到完整地址呢?再打开连播时的网页,查看源文件。可以发现:
    <div id="playlist">正在读取列表,请稍后.....(如果很长时间未读出,请按“F5”刷新)</div>
    <div id="gj_2"><span id="url"><a href="#" class="abuts1" id="bg_fs">分享</a></span><a href="#" id="bg_sc_lb" class="abuts1">收藏列表</a><span id="MusicGeCi"><a href="#" class="abuts1" id="bg_gc">歌词</a></span>
      <div id="gj_bf"><input id="loop" type="checkbox" onclick='changeClassName(this,document.getElementById("dxhs"),"hs");loopp();' /><label for="loop" id="dxhs" onmousemove='this.className="hs"' onmouseout='if(!document.getElementById("loop").checked) this.className=""'>单曲循环</label> <input  type="checkbox" value="" id="ran" onclick='changeClassName(this,document.getElementById("sjbs"),"hs");ran();' /><label for="ran" id="sjbs" onmousemove='this.className="hs"' onmouseout='if(!document.getElementById("ran").checked) this.className=""'>随机播放</label></div>
  </div></div>
  <div id="right"><div id="diange"></div><div id="wmusicad"></div></div>
<div id="foot"></div>
</div>
</body> 
<SCRIPT language="JavaScript" src="[COLOR="Red"]/js/lianbosy.js[/COLOR]"></SCRIPT>
</html>

加红的部分就是存放有关键信息的js文件,OK我们把它下载回来,可以看到如下内容:
		var STAND_STR = "/19z/",
		STAND_STR2 = "/20z/",
		STAND_STR3 = "/21z/",
		STAND_STR4 = "/22z/";

		if(url.indexOf(STAND_STR)==-1 && url.indexOf(STAND_STR2)==-1 && url.indexOf(STAND_STR3)==-1 && url.indexOf(STAND_STR4)==-1) 	
                                       ht = "http://ow.haoting.com";
		else										       ht = "http://wma.haoting.com";

没错它的wma服务器就只有2个,哪首歌曲存放在哪个服务器上的判断条件也给出来了,就是判断前5个字符,如果符合要求就用http://wma.haoting.com,否则用另外一个.现在煮饭用的锅碗瓢盆都已经有了,就差一把烈火了,我们只要通过分析音乐播放页面的地址,从中找到歌曲ID,再从相应的JS文件中分析出下载地址,歌曲名,歌手名,所属专辑。就大功告成了,这个方法对好听音乐网站上的所有歌曲适用。
Now is Code time !........
要是连代码一起贴出来,整个帖子就太长了,直接上个效果图

源码和程序都在附件里提供了,加了个把下载地址导出为下载列表的功能,可以用迅雷导入来实现批量下载。
Good luck !
上传的附件:
2009-6-28 16:17
0
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
10
过来支持一下。
再膜拜下LZ的学习精神~~~~
2009-6-28 22:36
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
谢谢大牛,
2009-6-29 09:49
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
LZ可真是穷追不舍啊。只是将原来破船的程序稍微改进了下 其实原理是一样的
2009-6-30 22:10
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
13
我其实没有看破船的代码,只看了他的分析网站的那段,是同一原理的不同实现而已
2009-6-30 22:52
0
雪    币: 411
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
不错。谢谢LZ哦。
2009-7-1 10:22
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
15
我实现的是这样的,用CString实现,简单快捷,而且屡试不爽:
UINT WINAPI WorkThread(LPVOID  Param)// need a point
{ THREADDATA * data=(THREADDATA * )Param;
  CString Url=L"http://haoting.com/musicjs/";
  int len=data->ID.GetLength();//ID就是歌曲ID了
 Url+=data->ID;
  Url+=L".js";
  //OutputDebugString(Url.GetBuffer(0));
  if(data->ID.IsEmpty())
   return 0;
HINTERNET Internet=::InternetOpen(L"DownloadMulic",INTERNET_OPEN_TYPE_DIRECT
,NULL,NULL,0);
if(Internet==0)
{OutputDebugStringA("open Internet failed\n");
   return 0;
}
HINTERNET hUrl=::InternetOpenUrl(Internet,Url,NULL,0,INTERNET_FLAG_RELOAD,0);
if(hUrl==NULL)
{   OutputDebugString(L"打开URL失败!");
	InternetCloseHandle(Internet);
	return 0;
}
PVOID buffer=malloc(1024);
memset(buffer,0,1024);
DWORD ret=0;
if(!::InternetReadFile(hUrl,buffer,1024,&ret))
{
	InternetCloseHandle(hUrl);
	InternetCloseHandle(Internet);
	return 0;
}
CString ALL,Temp=L"http://wma.haoting.com";
WCHAR * widechar=new WCHAR[1024];
MultiByteToWideChar(CP_ACP,0,(char*)buffer,strlen((char*)buffer),widechar,1024);
ALL=widechar;
//AfxMessageBox(ALL);
int Pos=0;
Temp+=ALL.Right(ALL.GetLength()-ALL.Find(L"url_")-6-len);
ALL=Temp;
Temp=Temp.Left(Temp.Find(L"\";"));
data->Dlg->m_List.SetItemText(data->Index,4,Temp);//地址
Temp.Empty();
//OutputDebugString(ALL.GetBuffer(0));
Temp=ALL.Right(ALL.GetLength()-ALL.Find(L"music")-8-len);
ALL.Empty();
ALL=Temp;
Temp=Temp.Left(Temp.Find(L"\";"));
data->Dlg->m_List.SetItemText(data->Index,1,Temp);//歌名
Temp.Empty();
Temp=ALL.Right(ALL.GetLength()-ALL.Find(L"singer")-9-len);
ALL.Empty();
ALL=Temp;
Temp=Temp.Left(Temp.Find(L"\";"));
data->Dlg->m_List.SetItemText(data->Index,2,Temp);//歌手
Temp.Empty();
Temp=ALL.Right(ALL.GetLength()-ALL.Find(L"specialName")-14-len);
ALL.Empty();
ALL=Temp;
Temp=Temp.Left(Temp.Find(L"\";"));
data->Dlg->m_List.SetItemText(data->Index,3,Temp);//专辑
Temp.Empty();
CString FileName;
FileName=data->Dlg ->m_FolderPath;
FileName+=L"\\";
Temp=data->Dlg->m_List.GetItemText(data->Index,1);
FileName+=Temp;
Temp.Empty();
Temp=data->Dlg->m_List.GetItemText(data->Index,4);
Temp=Temp.Right(4);
FileName+=Temp;
data->Dlg->DownLoadToFileW(0,NULL,0,data->Dlg->m_List.GetItemText(data->Index,4).GetBuffer(),FileName.GetBuffer(0));//直接下载一起搞定,多线程socket下载,速度和迅雷一样快
delete widechar;
int pos=data->Dlg->m_Progress.GetPos()+1;
data->Dlg ->m_Progress.SetPos(pos);
free(buffer);
return 1;
2009-7-1 22:10
0
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
sdk 里没有MFC的类用。。。
2009-7-2 10:35
0
雪    币: 104
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
  学习 ~~
2009-8-9 23:43
0
游客
登录 | 注册 方可回帖
返回
//