大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

python爬虫-爬取高逼格音乐网站《落网》

关于

落网,一个高逼格的音乐网站,是很多文艺青年经常逛的网站。经常下载落网音乐到本地,一首一首的下载十分的痛苦。最近刚好在学习python爬虫的东西,刚好把落网音乐的爬取当做一个练习项目来实践(IT从业者的好处~)。

准备

工具 :python 2.7,PyCharm

类库:urllib2、BeautifulSoup、time、re、sys

分析

要想下载落网的音乐,首先要获取每首音乐的url,通过chrome浏览器的开发者工具(F12)查看网页的源代码可以看到图片的URL,没有音频的URL,如图

image
image

但是,我们发现点击每首歌曲的时候,network都会一个音频的请求,如图

image
image

那么很显然,这个就是我们需要的下载URL地址了。通过分析http://mp3-cdn2.luoo.net/low/luoo/radio924/02.mp3这个地址,我可以知道前面部分http://mp3-cdn2.luoo.net/low/luoo/是固定的地址,后面的radio924/02.mp3中924是每个期刊的期刊号,其中02是每个期刊下面的第几首歌。要想获取同类型期刊(比如rock),URL为http://www.luoo.net/music/rock,首先我们要获取每个期刊的期刊号于期刊URL的对应关系,通过抓取HTML源码来获得,如图

image.png
image.png

代码如下:
    #获取每个期刊URL与频道的对应关系,存放在一个dict中
    def getReation(self, pageIndex):
        soup = self.getPage(pageIndex, '')
        vols = {}
        pattern = re.compile('[0-9]+')  //正则匹配获取HTML源码中的期刊号
        if not soup:
            return None
        vol_lists = soup.find_all('div', class_='meta')
        for vol in vol_lists:
            vol1 = re.search(pattern, str(vol.a.get_text())).group()
            vols[vol1.strip()] = vol.a['href']
        return vols

然后根据上面的对应关心来获取每首歌的信息,同样存放在一个dict中,以songName为key,以URL为value,如下。

    #获取每首歌的名称和url
    def getSongInfo(self, vols):
        songInfos = {}
        for vol in vols.keys():
            url = vols[vol]
            soup = self.getPage(0, url)
            total = len(soup.find_all('li', class_='track-item'))
            songNames = soup.find_all('a', class_='trackname')
            for i in range(1, total+1):
                songName = self.delSongName(songNames[i - 1].get_text())
                if i < 10:  //对前面【1-9】首的URL进行格式化。
                    songURL = self.music_url + 'radio' + str(vol).lstrip('0') + '/0' + str(i) + '.mp3'
                else:
                    songURL = self.music_url + 'radio' + str(vol).lstrip('0') + '/' + str(i) + '.mp3'
                songInfos[songName] = songURL
        return songInfos

获取到每首歌的信息后,然后我们就可以正式的进行下载了(哈哈,终于到正题了~),如下

    def downloadSong(self):
        totalPage = self.getTotalPage()  //首先获取该类型期刊的总的页数
        for pageIndex in range(1, int(totalPage)+1):
            vols = self.getReation(pageIndex) //获取每一页的期刊号与URL的对应关系
            songInfos = self.getSongInfo(vols)  //获取歌曲信息name和URL
            for songName, songURL in songInfos.items():
                time.sleep(5)
                print('%s 正在下载中。。。' %(songName))
                try:
                    data = urllib2.urlopen(songURL).read()
                except urllib2.URLError:
                    print("######链接不存在,继续下载下一首########")
                with open (('D:\\test\\song\\%s.mp3' %(songName)).decode('utf-8'), 'wb') as f:
                    f.write(data)

总结
到这边,基本上,对爬取每个类期刊下面的歌曲有了一个整体的思路,大家可以根据这些来实现自己的爬虫额。
下面给出本人的实现代码,给大家一个参考。

#!-*- coding: utf-8 -*-

import urllib2,urllib
from bs4 import BeautifulSoup
import re
import time
import sys

reload(sys)
sys.setdefaultencoding( "utf-8" )

class DownloadSong(object):
    def __init__(self,base_url):
        self.url = base_url
        self.music_url = 'http://mp3-cdn2.luoo.net/low/luoo/'
        self.pageIndex = 1
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
        }

    #获取页面的源码
    def getPage(self, index, vol_url):
        if index != 0:
            url = self.url + '?p=' + str(index)
        else:
            url = vol_url
        try:
            request = urllib2.Request(url, headers=self.headers)
            response = urllib2.urlopen(request).read()
            soup = BeautifulSoup(response, 'html.parser')
            return soup
        except urllib2.URLError, e:
            if hasattr(e, 'reason'):
                print(u'链接失败,失败原因', e.reason)
                return None

    #获取总页面数
    def getTotalPage(self):
        soup = self.getPage(self.pageIndex, '')
        if not soup:
            return None
        totalPage = soup.find_all('a', class_='page')[-1].get_text().strip()
        return totalPage

    #处理歌曲的名称
    def delSongName(self, songName):
        return songName.split('.')[1].lstrip().encode('utf-8')

    #获取每个期刊URL与频道的对应关系
    def getReation(self, pageIndex):
        soup = self.getPage(pageIndex, '')
        vols = {}
        pattern = re.compile('[0-9]+')
        if not soup:
            return None
        vol_lists = soup.find_all('div', class_='meta')
        for vol in vol_lists:
            vol1 = re.search(pattern, str(vol.a.get_text())).group()
            vols[vol1.strip()] = vol.a['href']
        return vols

    #获取每首歌的名称和url
    def getSongInfo(self, vols):
        songInfos = {}
        for vol in vols.keys():
            url = vols[vol]
            soup = self.getPage(0, url)
            total = len(soup.find_all('li', class_='track-item'))
            songNames = soup.find_all('a', class_='trackname')
            for i in range(1, total+1):
                songName = self.delSongName(songNames[i - 1].get_text())
                if i < 10:
                    songURL = self.music_url + 'radio' + str(vol).lstrip('0') + '/0' + str(i) + '.mp3'
                else:
                    songURL = self.music_url + 'radio' + str(vol).lstrip('0') + '/' + str(i) + '.mp3'
                songInfos[songName] = songURL
        return songInfos

    #下载歌曲
    def downloadSong(self):
        totalPage = self.getTotalPage()
        for pageIndex in range(1, int(totalPage)+1):
            vols = self.getReation(pageIndex)
            songInfos = self.getSongInfo(vols)
            for songName, songURL in songInfos.items():
                time.sleep(5)  //适当的减慢下载速度,不要给人家服务器造成压力。
                print('%s 正在下载中。。。' %(songName))
                try:
                    data = urllib2.urlopen(songURL).read()
                except urllib2.URLError:
                    print("######链接不存在,继续下载下一首########")
                with open (('D:\\test\\song\\%s.mp3' %(songName)).decode('utf-8'), 'wb') as f:
                    f.write(data)

if __name__ == '__main__':
    url = 'http://www.luoo.net/music/classical'  //传入古典期刊的url
    downloadsong = DownloadSong(url) //生成一个对象
    downloadsong.downloadSong()  //调用downloadSong方法来正式下载额。

结果如下


image.png
image.png

image.png
image.png

谢谢大家,此处有掌声。哈哈~