天天看点

python实例豆瓣音乐代码_扫描"豆瓣音乐"所有专辑的简单Python脚本

我需要一些音乐数据,而 豆瓣音乐 上相关数据是非常齐全的。

即使是十分小众、鲜有人听过的专辑,豆瓣也提供了较为详细的描述。

摆在我面前的第一个问题是,如何将豆瓣音乐上的专辑一网打尽,一个不遗漏地抓取到。

看到subject后跟着一串数字ID,这时候一个简单的思路已经出来了:

将id从1不断递增,扫描完所有的ID,检查哪些ID是存在的

又观察到大多数专辑页面上存在相关推荐,也就是那个“喜欢某某专辑的人也喜欢… …”。

这里存在一对N的超链接映射,最终形成了网络,于是可以有第二个思路:

将某个专辑加入队列,从它出发,通过 “也喜欢” 来获取新专辑,并将这些新专辑加入队尾

每完成一次操作,就从队列中删除队首的专辑

循环上面的过程,直到处理完队列中的所有元素

最终获取到的就是包含第一个元素的网

当然,上面的思路是有缺陷的,那些孤立的专辑无法被包括进来。

我决定使用ID自增的方式来扫专辑。

在python中,直接使用urllib2.urlopen去下载完整的HTML页面是不太经济的,

特别是一开始我并不需要解析HTML,我只是想知道这个页面是否存在。可以使用head请求。

我还观察到直接访问www.douban.com/subject/id可能会产生一个302的重定向,或者404错误

于是我可以通过请求www.douban.com/subject/id/来确定对应ID是否是一个音乐专辑的ID

302还可能是重定向到其他douban.com二级域名,比如book、movie,但我只需要music,其他的直接忽略了。

(获取豆瓣所有的电影和图书也很方便,不是吗?)

MySQL下创建表album:

CREATE TABLE `album` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`album_url` int(10) unsigned NOT NULL,

PRIMARY KEY (`id`),

KEY `idx_album_id` (`album_url`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

它包括id字段和album_url字段,但album_url是int,并非varchar,因为我只需要保存数字id。

这里有一点小的技巧:

headers伪装成了goolebot,减小IP被屏蔽的几率

id从1扫描到3000万,这是个估值上界,通过查看豆瓣最近收录的新专辑判断

同步队列id_queue最大的长度是100,不要忽略了参数,设置成无限长度,3000万太多

这里有50个线程同时工作,但是数据库连接dbconn是全局的、公用的,使用锁lock来确保每次只有一个线程进入

在id_queue中加入了50个None,作为结束标志

可以直接HEAD请求music.douban.com的页面判断是否存在,404表示这个id不存在、302表示这个id是其他内容而不是音乐,只有status返回200才表示对应的专辑存在

使用上面的脚本,从昨晚运行到现在,我已经扫描到了70万个专辑。 🙂