【背景】
先说的是,自己之前写了个小软件,用于下载songtaste中的歌曲:
最新版本是1.5:
downloadSonstasteMusic(下载Songtaste歌曲) v1.5 – 下载Songtaste(ST)中正在播放的歌曲/单首歌曲/整张专辑
最近发现,有个特殊的songtaste中的歌曲,无法下载。
对应地址为:
http://www.songtaste.com/song/2169436/
所以,就需要去搞懂为何无法下载。
就需要用到工具去分析背后的逻辑。
所以此处就去记录,或者说顺带演示一下,如何通过IE9的F12,去分析抓取,该首songtaste歌曲的真实url地址,以及这个地址是如何产生的。
提示:
1.不了解songtaste和想要下载songtaste中的歌曲的,自己去看:
2.关于网页抓取等相关逻辑不了解的,自己去看:
【整理】关于抓取网页,分析网页内容,模拟登陆网站的逻辑/流程和注意事项
【教程】手把手教你如何利用工具(IE9的F12)去分析模拟登陆网站(百度首页)的内部逻辑过程
【教程】如何利用IE9的F12去分析网站登陆过程中的复杂的(参数,cookie等)值(的来源)
下面记录整个过程:
1.打开IE9,打开F12,开始捕获,输入对应的上面的url地址,然后输入回车或点击Go:
2.然后就可以看到抓取的结果了:
其中,上述ST的flash播放器,已经载入完毕,且已可以看到歌曲信息和播放时长。
说明,此时,真正的歌曲地址,已经获得了。
所以,接下来,就是,想要搞清楚,如何获得对应的歌曲的地址了。
3.此处,由于不知道具体的歌曲地址的值是什么,所以无法通过搜素某个关键字,而找到对应的歌曲地址。
但是此处由之前的知识所了解到了,对应的歌曲,十有八九是.mp3后缀。
所以,就先去找找.mp3,看看能否找到有价值的参考信息:
可以搜到一些相关的内容:
然后我们通过点击向右的箭头,一点点继续看下一个:
然后看到跳转到对应的下一个了:
如此,一点点往下找,最后,我们可以找到一个,看起来像我我们所要的结果:
4.然后,对于此处,看起来,像我我们所要找到歌曲的真正的地址:
我们好好去分析一下,此处对应的Response Body,即访问对应的url,所返回的结果,是如何获得的。
即对应的headers,cookie等相关信息,都是什么。
如果是POST,以及对应的post data都是什么。
提示:
不了解相关的header,cookie,post data的,可以去参考:
【整理】网页抓取,模拟登陆,抓取动态网页内容等过程中,所涉及的Headers信息,Cookie信息,POST数据的处理逻辑
要访问的url
url地址是:
http://www.songtaste.com/time.php
Request Headers:
截图:
内容:
Key Value |
Request Body,即Post data:
截图:
内容:
| str=dcc505ff224130fe0ff0bb9bd7ffa907ef14c468ffae8e61b62d70f7043f13ced95bd52afec72ff62f1ced3fe51ffc51&sid=2169436&t=0 |
有了
url
headers(其中包含了cookie)
post data
就可以去实现对应的代码,去模拟此过程了。
但是很明显,在写代码模拟过程之前,还需要搞懂,其所headers,cookie,post data中的值,都是如何来的,如何得到的,才能写代码模拟出来。
此处,以此去分析一下相关的数据。
此处,最关心的数据,明显是Post data中的各个值,所以先去分析这些:
str=dcc505ff224130fe0ff0bb9bd7ffa907ef14c468ffae8e61b62d70f7043f13ced95bd52afec72ff62f1ced3fe51ffc51
去搜dcc505ff224130fe0ff0bb9bd7ffa907ef14c468ffae8e61b62d70f7043f13ced95bd52afec72ff62f1ced3fe51ffc51,最后可以找到出现的位置:
所以就是,我们可以通过访问:
http://www.songtaste.com/song/2169436/
然后从返回的html中,提取此处我们需要的这个值,即可。
这个很容易理解。
sid=2169436
这个更明显,2169436,就是songtaste歌曲的id,即本身所访问的url:
http://www.songtaste.com/song/2169436/
中的2169436。
所以也可以从url中提取出来。
t=0
t的值,很明显,固定都是设置为0.所以代码中,也可以设置为0即可。
所以,此处,加上之前所分析的songtaste中,关于获得对应的歌曲地址的方法,
再把此处的用于获取:
http://www.songtaste.com/song/2169436/
的歌曲真实地址的逻辑:
| 要访问的url | 方法(GET或POST) | (如果是POST,所需的)post data | 必须的headers | 可能需要的cookie |
| http://www.songtaste.com/song/2169436/ | POST |
| 暂时可以试试,只是使用普通的header,看看能否正常获得返回值 目前觉得必须要有的是: | 暂时可以不发送cookie,看看是否可以正常获得所需数据 |
如此,就算是获得了对应的逻辑了。
如何从songtaste的歌曲的页面地址:
http://www.songtaste.com/song/2169436/
中,获得歌曲的,当时播放时刻的,真实地址:
提示:此地址过段时间就会失效,所以属于临时的,真实的地址,有时效性的。
所以,无法用于永久的外链的。
对应的代码实现,之前就已经实现了,摘录过来:
// idOrUrl : 955407 or http://www.songtaste.com/song/955407 or http://www.songtaste.com/song/955407/
// songInfo: the ST song info: title, artist, realAddr, ...
// errStr: error reason string
public bool getSongInfo(string idOrUrl, out songInfo songInfo, out string errStr)
{
bool getInfoOk = false;
errStr = "未知错误!";
songInfo = new songInfo();
string songId = "";
string songUrl = "";
if (!isValidIdOrUrl(idOrUrl, out songId, out songUrl))
{
errStr = "无法识别的Songtaste的Id或Url:" + idOrUrl + " !";
return getInfoOk;
}
else
{
songInfo.id = songId;
songInfo.url = songUrl;
}
// here must clear previous cookies
// otherwise access html with previous cookies will get fault html:
//信息提示: 对不起,该用户不存在! 3 秒钟以后系统将自动跳转!
crl.clearCurCookies();
string respHtml = "";
respHtml = crl.getUrlRespHtml(songInfo.url, stHtmlCharset);
if (respHtml != "")
{
//note: some song is illegal:
//http://songtaste.com/song/3029002/
//return invalid html content
//following can not found valid info
// 1. get song title and artist
//<p class="mid_tit">╰.很多人都在找的一首伤感韩文丶最近很火﹀淑熙(啦啦啦)丶</p><p></p>
string titleP = @"<p\s+?class=""mid_tit"">(?<title>.+?)</p>";
Match foundTitle = (new Regex(titleP)).Match(respHtml);
if (foundTitle.Success)
{
songInfo.title = foundTitle.Groups["title"].Value;
songInfo.title = crl.removeInvChrInPath(songInfo.title);
songInfo.title = songInfo.title.Trim();
//<h1 class="h1singer">1956.烟蒂</h1>
//<h1 class="h1singer">Rie fu</h1>
//<h1 class="h1singer"></h1>
string singerP = @"<h1\s+?class=""h1singer"">(?<singer>.*?)</h1>";
Match foundSinger = (new Regex(singerP)).Match(respHtml);
if (foundSinger.Success)
{
songInfo.artist = foundSinger.Groups["singer"].Value;
//special: http://www.songtaste.com/song/809103/, no singer
if (songInfo.artist == "")
{
songInfo.artist = "Unknown Singer";
}
songInfo.artist = crl.removeInvChrInPath(songInfo.artist);
songInfo.artist = songInfo.artist.Trim();
// 2. get song real address
//<a href="javascript:playmedia1('playicon','player', '5bf271ccad05f95186be764f725e9aaf07e0c7791a89123a9addb2a239179e64c91834c698a9c5d82f1ced3fe51ffc51', '355', '68', 'b3a7a4e64bcd8aabe4cabe0e55b57af5', 'http://m3.', '3015123',0);ListenLog(3015123, 0);">
//http://www.songtaste.com/song/2428041/ contain:
//<a href="javascript:playmedia1('playicon','player', 'cachefile33.rayfile.com/12f1/zh-cn/download/d1e8d86a0a9880f697aee789f27383db/preview', '355', '68', 'b3a7a4e64bcd8aabe4cabe0e55b57af5', 'http://224.', '2428041',0);ListenLog(2428041, 0);">
//string songP = @"javascript:playmedia1\('playicon','player', '(?<str>\w+)', '\d+', '\d+', '\w+', '.+?', '(?<sid>\d+)',\d+\);";
string songP = @"javascript:playmedia1\('playicon','player', '(?<str>[^']+)', '\d+', '\d+', '(?<keyStr>\w+)', '(?<urlPref>.+?)', '(?<sid>\d+)',\d+\);";
Regex songRx = new Regex(songP);
Match foundSong = songRx.Match(respHtml);
if (foundSong.Success)
{
string str = foundSong.Groups["str"].Value;
string urlPref = foundSong.Groups["urlPref"].Value;
string keyStr = foundSong.Groups["keyStr"].Value;
string sid = foundSong.Groups["sid"].Value;
if (str.Contains("/"))
{
//cachefile33.rayfile.com/12f1/zh-cn/download/d1e8d86a0a9880f697aee789f27383db/preview
//to get the suffix
string suffix = "";
string mainJsUrl = "http://image.songtaste.com/inc/main.js";
string respHtmlMainJs = crl.getUrlRespHtml(mainJsUrl);
// case "b3a7a4e64bcd8aabe4cabe0e55b57af5":
// return ".mp3";
string suffixP = @"""" + keyStr + @""":.+?return\s+""(?<suffix>\.\w+)"";";
Regex suffixRx = new Regex(suffixP, RegexOptions.Singleline);
Match foundSuffix = suffixRx.Match(respHtmlMainJs);
if (foundSuffix.Success)
{
suffix = foundSuffix.Groups["suffix"].Value;
songInfo.realAddr = urlPref + str + suffix;
}
}
else
{
//5bf271ccad05f95186be764f725e9aaf07e0c7791a89123a9addb2a239179e64c91834c698a9c5d82f1ced3fe51ffc51
Dictionary<string, string> headerDict = new Dictionary<string, string>();
headerDict.Add("x-requested-with", "XMLHttpRequest");
// when click play
// access http://songtaste.com/time.php, post data:
//str=5bf271ccad05f95186be764f725e9aaf07e0c7791a89123a9addb2a239179e64c91834c698a9c5d82f1ced3fe51ffc51&sid=3015123&t=0
Dictionary<string, string> postDict = new Dictionary<string, string>();
postDict.Add("str", str);
postDict.Add("sid", sid);
postDict.Add("t", "0");
string getRealAddrUrl = "http://songtaste.com/time.php";
songInfo.realAddr = crl.getUrlRespHtml(getRealAddrUrl, headerDict, stHtmlCharset, postDict);
}
if (songInfo.realAddr != "")
{
int lastPoint = songInfo.realAddr.LastIndexOf('.');
int strLen = songInfo.realAddr.Length;
songInfo.suffix = songInfo.realAddr.Substring(lastPoint);
songInfo.storedName = songInfo.title + " - " + songInfo.artist + songInfo.suffix;
getInfoOk = true;
}
else
{
errStr = "找不到歌曲的真实下载地址!";
}
}
else
{
errStr = "找不到歌曲的真实下载地址!";
}
}
else
{
errStr = "找不到歌曲的歌手信息!";
}
}
else
{
errStr = "找不到歌曲的标题信息!";
}
}
else
{
errStr = "无法获取网页信息!";
}
return getInfoOk;
}
完整的代码,感兴趣的自行参考:
http://code.google.com/p/downloadsongtastemusic/source/browse/#svn%2Ftrunk
注:不了解google code的,自己去看:
另外,要说一下,本来开始说的是,此处的歌曲:
http://www.songtaste.com/song/2169436/
无法下载,以为是获得对应歌曲的播放地址的算法和之前不一样呢。
结果经过刚才的分析,得到的逻辑,和以前是一样的。
而无法下载歌曲的原因,是另外别处的代码的问题:
在申请了一个100M的buffer用于存放从songtaste的歌曲的数据,
结果由于此歌曲是160多MB,导致之前的100M的buffer不够用,而报错了。
这就是别的问题,需要我去想办法解决该问题。和此处的逻辑无关了。
【总结】
所以,简而言之,上述通过IE9去分析,如何从songtaste的url获得歌曲播放地址的逻辑,都是正确的。
而关于具体实现代码,去参考上面给出的google code上面的代码,即可。