周围好多朋友都用这个听歌,可就是歌曲不能下载,是不是很痛苦,手机、平板上的App也只能缓存100MB,不过瘾是不是,还是自己动手吧。
短暂分析了下过程,思路很简单:抓包分析URL -- 提取歌曲封面及地址 -- 保存到本地。
抓包
蟒蛇下pcap和dpkt就是万金油,程序的逻辑上要注意的有2点,首先要把无关host的请求url都pass掉,第二记得处理一下User Agent,否则你分析得到文件的URL在后续下载过程中会被主程序再次抓包记录,就变成了死循环。。。
def main_pcap(p_time, p_data):
out_format = "%s\t%s\t%s\t%s\t%s"
p = dpkt.ethernet.Ethernet(p_data)
ret = None
if p.data.__class__.__name__ == 'IP':
ip_data = p.data
src_ip = '%d.%d.%d.%d' % tuple(map(ord,list(ip_data.src)))
dst_ip = '%d.%d.%d.%d' % tuple(map(ord,list(ip_data.dst)))
if p.data.data.__class__.__name__=='TCP':
tcp_data = p.data.data
if tcp_data.dport==80:
if tcp_data.data:
try:
h = dpkt.http.Request(tcp_data.data)
host = h.headers['host'].strip()
agent = h.headers['user-agent'].strip()
pre = '.*douban.com'
if match(pre, host) and agent.startswith('Mozilla'):
url = "http://" + host + h.uri
ret = out_format % (p_time, src_ip, dst_ip, h.method, url)
log(p_time, url, host[0:host.index('.')])
return (ret, url)
except Exception, ex:
clr.print_red_text("Exception: "+str(ex))
后面的主要工作就是根据抓包得到的URL去下载了,代码不多贴了,注意的是需要异步方式执行,否则主程序就block了,这里介绍一种python利用装饰器和threading实现异步调用的方面,非常滴Pythonic
def async(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
my_thread = threading.Thread(target=func, args=args, kwargs=kwargs)
my_thread.start()
return wrapper
程序从上手到完成一共花了不到2个半天,遇到最主要的问题就是dpkt库对http request header的解析有问题,虽然可以用的try…exception解决问题,但是这并不是正确面对问题的态度,还好google是万能的,大家详见Issue 90: dpkt http wrong parse_headers logic
后续的问题
歌曲和封面都下载下来了,发现坑爹的是豆瓣居然把文件的ID3都给清除了,那么多歌我那记得哪首是哪首啊,还要找到了个好东东,Quintessential Media Player,能根据歌曲的波形来自动匹配,数据库也足够强大,请自行收藏之。
需要脚本的话,请email我吧,用的话你需要先安装Python 2.7、pcap和dpkt,其他真没有了。