【背景】
先说的是,自己之前写了个小软件,用于下载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上面的代码,即可。