PyETGO 专辑下载脚本 Python练习作品


PyETGO 0.3 更新版下载:http://code.google.com/p/ptcoding/source/browse/trunk/PyETGO

本脚本可跨平台使用,在Win下需要wget for Windows, 把wget.exe放在脚本所在目录即可。
Wget下载:http://www.interlog.com/~tcharron/wgetwin-1_5_3_1-binary.zip

感谢Twitter上的好友mengzehe关于Win下使用的测试和提醒。
感谢Ubuntu论坛上网友的测试。
v0.3
2009.03.16
    -对获取的XML列表中存在的非法字符进行过滤(解决曲名含”&”等不规则字符导致无法下载)

v0.2更新

  • 2009.03.11 
  • -为在Win下使用本脚本,全部使用Unicode的字符串来提示(0.1有部分乱码)
  • -修改写入Intro.txt文件的方法,使用writelines,会根据操作系统不同写入不同换行符,减少乱码
  • -写入文件名前过滤Win下的非法文件名字符/\:<>?|等

大概实现这样的功能:

  1. 下载http://music.etgo.cn/上的任意专辑的音乐文件
  2. 专辑的存放目录命名为“歌手名 – 专辑名”
  3. 多CD的专辑,音乐文件命名为“CD号-轨号_歌手 – 歌名”,单CD则为“轨号_歌手 – 歌名”
  4. 下载专辑的封面和封底文件cover.jpg、coverback.jpg
  5. 从页面中抽出的专辑信息和介绍文字写入Intro.txt
  6. 首次使用需要用-u和-p输入一个ETGO账户以获取Cookie,以后下载只需用-a指定一个专辑的页面
  7. 下载时可随时使用Ctrl + C中断,重新下载时自动从断点续传。

花了好几天的时间在这个脚本上,基本把Python的特性摸熟了。

ETGO是国内一个娱乐网站,有电影、Mp3等,资源不算新,格式也就192~256K那样,没太大特色,但其有自己的服务器,运营稳定,这几年来我偶尔都从那里Down些专辑,基本上浏览器的嗅探+序号批量下载就可搞定,而且速度不赖。网站的免费试听使用的是Flash的播放器,这个脚本则模拟了播放器读取列表的功能,骗回所有Mp3的原始地址,然后调用wget下载。

稍微记录下开发过程。

首先还是逆向路线,打开Wireshark在嗅探,从浏览器点击一首歌的试听,浏览器弹出窗口开始播放。从嗅探结果看,在本地发出GET MP3文件的那个报文之前,服务器必然已经把真实地址发回给Flash播放器,于是检查前一个请求:GET /mp3player.php。从其相应报文类型HTTP/XML就可以判断出这是一个返回XML信息的请求,打开分析,果然。

但这个mp3player.php根据什么来知道用户的点歌呢?查看专辑页的源码,只见每个“试听”链接具有这样的属性:

onClick=”MM_openBrWindow(‘/player.php?fid=90388′,’MusicPlayer’,’width=446,height=340′)”

不难猜到fid=54664就是每首歌的“身份ID”,player.php是带Flash控件的那个页面,fid应该是送给Flash的参数,而mp3player.php才是返回列表XML,而mp3player.php没有任何GET参数,说明请求还是藏在报文内。

回到Wireshark查看GET /mp3player.php那个报文,展开其请求,一眼看不出什么猫腻,就见到那个长长的Cookie,Wireshark没有给出完整的分析,直接看下面的原始数据:

抓到你了!竟然藏在Cookie的结尾。

1.getPlayListXML模块

有了之前写CETQuery的经验,模拟它发个这样的报文很轻松,稍作调试就收到完整的XML歌曲列表。原来ETGO整个网站对用户的验证都是通过这样的Cookie,里面甚至包含了明文的用户名和密码,都算的上是世界上最不安全的网站之一了。都几年了没点技术更新,难怪我骗起来那么容易(嘿嘿)。

一次得一首歌的效率我可是不满意的,页面里面不是有个连播么,一次往列表里面添加所有歌。同上道理嗅探下来,原来只需把藏在Cookie后面那个参数用%7C分隔,连上一串uid即可:

playlist=20274%7C20275%7C20276%7C20277%7C20278……

剩下的是解析XML的工作,这可是Python的强项,我选择了minidom。《Dive in Python》Scripts and Streams一章中示范了直接把urlopen的流对象直接塞给minidom的parse,帅呆了。操作方法和Javascript的DOM很类似,用了一个getElementsByTagName把所有个song元素调出,再组织一下属性值,轻松过关。

2.Download File 方法

如何下载Mp3文件呢?直接下载是不行的,服务器有HTTP头检测。wget –help看了一下,果然不愧是UNIX下的老牌工具,很强大,–header可定义下载时的HTTP头。依旧在Wireshark看其连接Mp3地址时的报文,看不出有什么特别的,也是那个Cookie。

3.getCookie 模块

要获得Cookie,需要在页面登录。页面登录经典的老牌方法,把用户名密码等post到http://member.etgo.cn/member.php,然后读取相应头部里面的’set-cookie’段,然后干脆把Cookie写入文件,以后要下载专辑都不用麻烦连服务器了,直接读取即可。

4.getAlbum 模块

如果仅仅是下载,这时已经完成了,但为了提高自动化程度,还得想办法自动获取每首歌的uid,这需要解释专辑的HTML页面。《Dive in Python》中的例子用的是SGMLParser,但是从文档看到说SGMLParser在2.6版本后就过时了,《Python网络编程基础》里面则用了HTMLParser,两个模块大同小异,选择了后者。

HTMLParser的设计和很多Python模块一样,预置了接口,只需要实现所用的部分即可(似乎就是传说中的抽象工厂模式)。为了下载的时候能够把文件名编得完美点,从网页中解析出完整的歌手、专辑名,用了一堆正则表达式来过滤。

5.模块内聚

以上的几个模块都实现完成后,虽说什么功能都不缺了,但还是耗了不少时间。一是文件名,对于多CD的专辑,在文件名前标上CD号,而单CD的,只标轨号。而CD号和轨号都是要从URL里面析取的,一晚上下来把正则表达式用得相当熟手了。其次是如何组织目录,检查文件,os模块内的几种系统调用,还有要响应强行终止的任务,处理命令行参数……

文章分类 Python 标签: , , ,
5 comments on “PyETGO 专辑下载脚本 Python练习作品
  1. 蒙泽和说道:

    我想问下,这个东西该怎么用呢?直接双击?

    • PT说道:

      在脚本的解压目录下执行./ETGO.py -a http://… -u user -p pass即可,分别是专辑的地址,和用户密码。参看里面的Readme吧!

  2. Shellex说道:

    赞一个。好东西

  3. reverland说道:

    已经消失了?……

  4. ayanmw说道:

    呵呵,http://music.etgo.cn 现在已经 This domain for sale 了。
    不知道 是否对其他站点有用呢。。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*