自动下载豆瓣FM的加心歌曲

平时比较喜欢听豆瓣电台的,于是加心了很多歌曲,和很多人一样都想把加心的歌曲下载到本地。毕竟豆瓣电台并不会循环播放你的加心歌曲的,于是搜了一下,发现网上只是通过某些手段获得加心歌曲的列表,然后再用其他的工具下载,这里有很多弊端,比如可能会有歌名一样的情况,而且有些小众歌曲无法或者说很难在网上搜到,于是就写了这个用于直接在豆瓣网上下载加心歌曲的小程序。

注意到加心的歌曲其实都是对应到某个专辑上的,并且我们可以让我们喜欢的专辑开始播放利用这个地址http://douban.fm/?context=channel:0|subject_id:2781176

然后手动的话就可以通过抓包来获取你想要的歌曲下载地址了,把这一系列动作写成程序就可以自动下载所有加心歌曲了。

程序使用了BeautifulSoup,可以在这里下载http://www.crummy.com/software/BeautifulSoup/#Download下载BeautifulSoup.py文件和本程序放在同一目录下即可。

下面是程序:

  1. # coding: utf-8
  2.  
  3. from BeautifulSoup import BeautifulSoup
  4. import urllib, urllib2, cookielib, re, json, eyeD3, os
  5.  
  6. num_p = re.compile(r'(\d+)')
  7. songs_dir = 'songs'
  8. base_url = 'http://douban.fm/j/mine/playlist?type=n&h=&channel=0&context=channel:0|subject_id:%s'
  9. invalid = ['/', '\\', ':', '*', '?', '"', '<', '>', '|']
  10.  
  11. def valid_filename(s):
  12.     return filter(lambda x:x not in invalid, s)
  13.  
  14. def get_songs_information(url):
  15.     subject_id = num_p.search(url).groups()[0]
  16.     ret = json.loads(urllib2.urlopen(base_url % subject_id, timeout=20).read())
  17.     return filter(lambda x:x['album'].endswith(subject_id+'/'), ret['song'])
  18.  
  19. def download(song):
  20.     try:
  21.         os.mkdir(songs_dir)
  22.     except:
  23.         pass
  24.     filename = '%s.mp3' % valid_filename(song['title'])
  25.     filepath = os.path.join(songs_dir, filename)
  26.     if os.path.exists(filepath):
  27.         return
  28.     urllib.urlretrieve(song['url'], filepath)
  29.     picname = song['picture'][1+song['picture'].rindex('/'):]
  30.     picpath = os.path.join(songs_dir, picname)
  31.     urllib.urlretrieve(song['picture'].replace('mpic','lpic'), picpath)
  32.     tag = eyeD3.Tag()
  33.     tag.link(filepath)
  34.     tag.header.setVersion(eyeD3.ID3_V2_3)
  35.     tag.encoding = '\x01'
  36.     tag.setTitle(song['title'])
  37.     tag.setAlbum(song['albumtitle'])
  38.     tag.setArtist(song['artist'])
  39.     tag.setDate(song['public_time'])
  40.     tag.addImage(3, picpath)
  41.     os.remove(picpath)
  42.     tag.update()
  43.  
  44. def html_decode(html):
  45.     #return html.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>').replace('&quot;', '"').replace('&#39;', "'")
  46.     import HTMLParser
  47.     return HTMLParser.HTMLParser().unescape(html)
  48.  
  49. def get(myurl, cookie):
  50.     opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookielib.CookieJar()))
  51.     urllib2.install_opener(opener)
  52.     req = urllib2.Request(myurl)
  53.     req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')
  54.     req.add_header('Cookie', cookie)
  55.     content = urllib2.urlopen(req, timeout=20).read()
  56.     soup = BeautifulSoup(str(content))
  57.     for div in soup.findAll('div', {'class' : 'info_wrapper'}):
  58.         p = div.find('div', {'class' : 'song_info'}).findAll("p")
  59.         sid = div.find('div', {'class' : 'action'})['sid']
  60.         try:
  61.             print "song:" + html_decode(p[0].string) + "\nsinger:" + html_decode(p[1].string) + "\nalbum:" + html_decode(p[2].a.string)
  62.         except:
  63.             print "song..."
  64.         mark = False
  65.         try:
  66.             for j in range(10):
  67.                 songs = get_songs_information(str(p[2].a))
  68.                 for song in songs:
  69.                     if sid == song['sid']:
  70.                         download(song)
  71.                         mark = True
  72.                         break
  73.                 if mark:
  74.                     break
  75.             if mark:
  76.                 print 'succeed!\n\n'
  77.             else: print 'fail!\n\n'
  78.         except Exception as e:
  79.             print e.message+'\n'
  80.  
  81. def main():
  82.     url = 'http://douban.fm/mine?start=%d&type=liked'
  83.     cookie = raw_input('cookie:')
  84.     print 'you should enter the pages you want to download'
  85.     page0 = int(raw_input('page from:'))
  86.     page1 = int(raw_input('page to:'))
  87.     for i in range(page1-page0+1):
  88.         get(url%((i+page0-1)*15), cookie)
  89.  
  90. if __name__ == '__main__':
  91.     main()

然后程序需要输入你的豆瓣电台的cookie和歌曲的开始和结束的页数(稍微响应下用户需求),当然对于很多人可能不知道如何获得cookie,那下面简单说一下。

利用chrome,进入豆瓣电台的页面然后右键审查元素,切入到network标签,然后再刷新一下网页(不要忘记是在登录状态下操作哦~)

然后左侧选择第一个文件,在header下,如上图就可以看到你的cookie了,ok尽情的下载你的加心歌曲吧!
----------------我是传说中的分割线------------------------------
scturtle童鞋的提示下,现在给mp3文件加入了歌手,专辑,封面图片等tag,然后完整的源代码压缩包可以在这里下载

ps:scturtle提醒说firefox下右键-查看页面信息-安全-查看cookie

pss:为了方便广大人民群众特推出exe版本,解压缩后点击里面的doubanfm.exe即可!

----------------再次更新的分割线请读我>_< -------------------------------- 由于豆瓣电台去掉了从某个专辑开始听的功能,所以呢之前的方法基本就是不能用了,但是再次注意到,豆瓣电台的分享,分享出去的链接是从某首歌开始播放的,但是这个需要歌曲的sid和ssid即可,sid很好搞,但是ssid就难了,这里要感谢猫鱼酱他写的豆瓣fm hacker的插件收集了很多的sid和ssid,并且提供了对外使用的接口,利用他的接口,这个脚本就可以再次安心的工作了。在这里下载最新的代码运行即可。

----------------2013年5月21日的再次分割线 --------------------------------

猫鱼酱的接口不能用了,于是现在自己写爬虫抓取ssid记录到自己的sae上,脚本可以工作了,但是由于爬虫工作刚刚开始,所以歌曲还不是很全,最新代码依旧在github上在这里下载最新的代码运行即可。

----------------2013年6月10日的再次分割线 --------------------------------

脚本再次更新,下载正常多了,而且加了个download_album.py可以下载一个专辑的歌曲,输入专辑的时候像这样输入即可 /subject/2978518/ ,最新代码依旧在github上在这里下载

----------------2013年9月3日的再次分割线 --------------------------------

每次都需要和豆瓣斗智斗勇啊。。。现在获取加心列表的难度有点大,所以需要多输入一个user_id_sign,首先进入加心列表页面,按F12,或者右键审查元素,点到console里,输入window.user_id_sign,然后吧值输入到脚本就好了,最后最新代码依旧在github上在这里下载

----------------2013年11月18日的再次分割线 --------------------------------

现在增加了kbps的选项,可以选64,128,192,只有买了pro的同学才能下载高品质,没买的同学只能下64的了,最后最新代码依旧在github上在这里下载

----------------2013年12月30日的再次分割线 --------------------------------

发现现在window.user_id_sign获取不到,现在是在加心列表页去获取window.SP这个值即可

您可能喜欢:
我猜您可能还喜欢:
, ,

《自动下载豆瓣FM的加心歌曲》有 155 条评论

  1. 测试可用啊……
    不过douban的文件都好小啊……
    话说像beautiful soup这种常用的还是放到site-packages比较好吧
    &你和超哥好像都要去pycon啊,知道的时候已经卖完票了,信息阻塞啊……

  2. 酱油众 | #5

    用了下,很赞!感谢作者

  3. 2222 | #6

    输入cookie 和页数后 窗口消失 怎么回事

  4. nian | #7

    小白..看不懂求教..下载了那个压缩包..然后iu就不知怎么做了

    • isnowfy

      @nian: 解压缩之后运行doubanfm.exe然后会让你输入cookie,和要下载的加心的歌曲的页数,cookie就是上文中说的用chrome获得,输入cookie后回车,之后输入页数页数就是如果你想下载加心列表里第2页至第5页的歌曲就先输2,回车,再输5回车

  5. youthcould | #9

    exe版本已经不能下载了,求发送一个,多谢

  6. 这个主要是有人不会获得cookes,但是也很简单了,这里又另外一个方法:http://blog.satikey.com/?p=194,访问:http://blog.satikey.com/tools/douban 可以获取你的加心歌曲

    • isnowfy

      @satikey: 之所以考虑用cookie而不是用用户名密码的方式,是因为用户名密码方式用一段时间之后可能会要求输入验证码就有点麻烦了

    • Ca doit se mesurer, en termes de tourisme & d’image de marque de Paris.J’ai jamais mis les pieds à Paris-Plage, mais c’est pas parce que ça ne m&sÃiuo;qnts©resre pas que ça ne fait pas venir du tourisme – qui constitue quand même une bonne partie du PNB…

  7. ycdoit | #11

    很不错哦,谢谢! 试了,下了几首歌了。。非常感谢,很棒的python脚本

  8. messtoy | #12

    只支持ID3里全为英文和汉语日语的呢,若是遇到罗马字母就自己关掉了啊,这个解决不了?

  9. fiammanda | #13

    经常出现failed怎么办……?还有请问怎样才能使下载完成之后窗口不要立刻关闭,好让我把文件列表和成功与否复制下来?
    谢谢!

    • isnowfy

      @fiammanda: fail的原因一个是网络原因,一个是豆瓣上已经没有这首歌了,你可以尝试重新下载,已经下载过的歌不会重复下载的,对于第二个问题你可以在命令行提示符下,定位到程序所在文件夹,输入“douban.exe”,引号不要输,就可以了

      • You are truly not alone Rick. The site was created to help those of us who do feel so alone. I know how hard it can be to open up. Just reading the site and sending your thofthguul note is a good step in the right direction! Keep reading the blog. Warmly, Dan

  10. ET | #14

    豆瓣FM不错,这篇文章也真不错~

  11. messtoy | #15

    下载的时候很快,就是不知道下载到哪里去了,原来那个版本不能下载日文和罗马文,现在这个却不知道下载哪里。

  12. 阿潘 | #18

    贪心下载了python3.3版本的,结果运行不了,乖乖换回了2.7~ 现在正嗷嗷下着呢,赞一个!

    • isnowfy

      @阿潘: 是啊,现在只能用2.7跑,3.*都会出问题的。。。

    • I think the clutterbug gene runs in fandom. Almost everyone I know is a collector of some kind, and it is terribly hard to let go of books.I might be a different person today if I had not aprtnepiced with Frank Kelly Freas. Loved him to death, but he was a hoarder. Scared the crap out of me to see what a state he was in. I swore I would never let it happen to me.

  13. lz,可否分享一下ssid的获取方法?

  14. yoyo杰杰克 | #22

    今天晚上又不行了,在py平台下运行程序时流量总是不跑,唉。。。。亏我还刚新攒了五页的红心,总是出现fail。

  15. yoyo杰杰克 | #23

    you should enter the pages you want to download
    page from:1
    page to:3
    song:Someday
    singer:Nickelback
    album:The Long Road

    song:What Goes Around.../...Comes Around Interlude
    singer:Justin Timberlake
    album:Futuresex/Lovesounds[Deluxe Edition]

    song:Spanish Song Bird
    singer:Keren Ann
    album:Not Going Anywhere
    fail!

    song:Peep Game
    singer:2Pac
    album:Strictly 4 My N.I.G.G.A.Z.
    这种情况到底是怎么回事,流量几乎不跑,跑了点又归零了,个别几首跑的都fail了,昨晚就一直fail,今天就不done也不fail总跳过,还是不能下载。

  16. yoyo杰杰克 | #24

    songs文件夹也不自己建立,我自己新建后也没用

  17. sky | #26

    不错,试了一下,就是不知道是不是网速的原因,怎么就卡在半路半天没有反应了。。。支持一下!

  18. __73__ | #27

    我用的是评论里贴出的最新代码,可是依然无法建立songs文件夹,虽然命令行界面显示了歌曲信息,也没有fail的提示,可是还是无法下载音乐

  19. __73__ | #28

    回上一条评论时又去试了下,这次成功了。。。

  20. O2 | #29

    不能用……提示:
    File "douban.py", line 69
    print "song:" + html_decode(p[0].string) + "\nsinger:" + html_decode(p[1].st
    ring) + "\nalbum:" + html_decode(p[2].a.string)
    ^
    SyntaxError: invalid syntax

  21. shadow | #30

    ubuntu下python版本是2.7.4
    输入cookies 和 页码后 咋没反映。也没报错。等了一会就回到当前目录了

  22. Jeremy | #31

    能不能直接导出链接然后用迅雷下载?那个比较快,找了两个抓链接的总是抓不全。
    http://xiaobude.com/app/douban/
    http://www.zyview.cn/doubanfm#
    第二个里面有个软件可以把数字还原成歌名,但是没有封面什么的。

  23. yoyo杰杰克 | #32

    我用了你给我的最新代码,不成,py v273,不显示失败也不是成功,songs文件夹依旧不自己建立,然后下载5月21那个版本,结果一样。

  24. yoyo杰杰克 | #33

    song:The Way You Move
    singer:OutKast
    album:Grammy Nominees 2004

    song:Old City (Instrumental)
    singer:The Shanghai Restoration Project
    album:The.Shanghai.Restoration.Project-Instrumentals

    song:MCMXXXVII
    singer:The Shanghai Restoration Project
    album:The.Shanghai.Restoration.Project-Instrumentals
    情况就是这样了

  25. 有批量下载加以工具http://www.appinn.com/douban-fm-xigongda-download/
    我想自动下载所听的每一首歌。边听边下载。

  26. 64位win8,输入完结束页后,过了20秒左右,自动退出了,啥都没有。

  27. Shouhua | #36

    Hi isnowfy,
    我有一点不明白,我们没有登录豆瓣音乐,单单使用cookie可以进入我的私人页面吗?

    • isnowfy

      @Shouhua: 我没有用用户名密码的方式是因为懒的去处理验证码的问题。。。至于cookie,就是你在用户名密码输入之后网站记住你的一种方式,有cookie网站就知道是你就不需要你再次输入用户名密码,所以用cookie也相当于你是以登录状态去访问,所以可以访问你的私人页面

  28. xiaoy | #37

    请问为什么我用你的脚本一开始获取不到歌曲列表

  29. Eyck | #38

    下载的音质的很不好,我买了pro 但是下载下来的还是64kps 这是为什么

    • isnowfy

      @Eyck: 对的,豆瓣电台的音质是挺低的,因为我没有买pro,所以我不知道pro的音乐地址是哪里。。。所以用我程序暂时只能下载低音质的,抱歉啊。。。

  30. 伊始 | #39

    非常感谢,终于下下来了,(*^__^*) 嘻嘻……对了,歌词可以下么? 0.0

  31. linsmalldragon | #40

    高手啊。果然好用。太感谢了

  32. Charles | #41

    请问下window.user_id_sign需要怎么填写?

    • isnowfy

      @Charles: 首先用chrome进入你的加心列表那个页面,点F12,点console,输入window.user_id_sign回车,把显示出来的值输入到程序即可

      • Charles

        @isnowfy: prereq = urllib2.Request('http://douban.fm?start=%sg%sg' % (sid, ssid))
        prereq.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')
        print urllib2.urlopen(prereq, timeout=60).read()
        req = urllib2.Request(base_url % kbps)
        req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')

        请问下先获取http://douban.fm?start=%sg%sg' % (sid, ssid)这个url有什么作用,能对后面的http://douban.fm/j/mine/playlist?type=n&sid=&pt=0.0&channel=0&from=mainsite&kbps=%d这个页面的爬取提供什么帮助吗?

    • isnowfy

      @Charles: 先访问那个,然后再访问playlist的时候playlist的第一首就是那首歌

      • Charles

        @isnowfy: 谢谢,请问下这个规律你怎么找到的呀?即

        1.http://douban.fm?start=%sg%sg' % (sid, ssid)这个页面怎么找到的哦?

        这个我找到了,通过豆瓣分享,就能知道url了。
        麻烦您看下我的第二个问题,谢谢~~

        2.怎么发现了先访问上面的url,在访问playlist的第一首就是要的歌曲信息了?
        我在豆瓣fm直接听当前歌曲的时候,查看playlist里面的东西居然不包含当前的听的歌曲,按道理当前听的一首歌访问的url和你上面提到的http://douban.fm?start=%sg%sg' % (sid, ssid)应该是一样的把?

    • isnowfy

      @Charles: 文章也提到了,豆瓣有个分享当前歌曲的功能,分享出去的链接就是从当前歌开始听

    • isnowfy

      @Charles: 我看了看,输cookie的时候输入加心列表那个页面的cookie就好了。。。

  33. 额。。。为什么我获取的window.user_id_sign是undefined。。。

  34. 楼主威武啊 我看代码去了

  35. halfcrazy | #44

    请问LZ,window.SP是在红心页查看源码得到的么?我这看到的怎么是这种值。
    window.SP = "::";

  36. yoyo杰杰克 | #45

    每次碰到有特殊符号或者特殊文字就会fail掉甚至干脆自己关闭,能不能不让程序出错后自动退出。

  37. yoyo杰杰克 | #46

    现在window.user_id_sign和window.SP都获取不了。完全无法下载了。

Trackbacks/Pingbacks:

  1. Definite Digest » 理解OAuth
  2. 教你如何获得你的豆瓣FM加心歌曲 | 翱翔 Beta
  3. 柯激情的个人主页 » 教你如何获得你的豆瓣FM加心歌曲
  4. Thought this was cool: 字符编码和中文乱码小叙 « CWYAlpha
  5. 浅析java的hashmap | 吃杂烩
  6. 理解OAuth | 吃杂烩
  7. 查找第K小的元素 | 吃杂烩
  8. Thought this was cool: 写python的c扩展简介 « CWYAlpha
  9. Thought this was cool: 打印自身的程序 « CWYAlpha

发表评论