您的位置:首页 > 编程语言 > Python开发

【python爬虫】网易云歌单下载(scrapy+selenium)

2018-12-07 15:34 826 查看

我又滚回来更新了,这一次我们的目标是网易云音乐,想要通过输入歌单的链接,然后把整个歌单的歌曲都下载下来,说做就做,看看这一次有会遇见怎样的问题把。
需要注意的点:

  1. 这一次使用的框架仍然是scrapy,不同于上个框架的是这一回加上了selenium,我是蛮不想借助这个的,但是让工具发挥他最大的用处,这才是我们该做的
  2. VIP才能下载的音乐依然无法下载,现在还没有那个实力,只能下载歌单中不是vip的音乐
  3. 本文用于技术交流,如果侵犯版权立即删除,任何人不能用于商业用途
    好的,那我们就开始吧,一开始我们先来分析一下网易云的歌单
    这是这一次要分析的网址: https://music.163.com/#/playlist?id=61650863 ,顺便卖一波安利,这个歌单蛮不错的,喜欢纯音乐的朋友可以听听看。

    首先我们要做的是获取这个歌单所有的歌曲的链接和名字,一开始觉得应该没有什么难度,
    就算歌单是动态加载的,找起来应该也没有什么难度,但是事实是对现在的我确实是蛮难的。
    右键网页源代码,搜索Rain after Summer(歌单第一首歌)发现果然是没有的

    接下来便是在f12中查找,确实发现了歌单的信息,本来接下来就是
response = requests.get(url,headers=DEFAULT_REQUEST_HEADERS)

但即使发所有的request headers都加上了,这部分内容页面没能显示出来,要是有朋友知道这是怎么回事,希望可以多多指教。
但是我们是不能在一颗树上吊死的,既然这个方法行不通,那么就使别的方法,这次我是使用了selenium,但是用这个的时候也遇见一个坑了,在使用selenium之后本来以为所有问题都会迎刃而解的,但是发现仍然搜索不到任何有关歌单的信息,就纠结测试了许久之后,终于发现了问题的所在,一切都源于他

这是一个iframe,歌单所有的消息都在这个里面,但是selenium是没有办法自动加载这个里面的内容的,这就导致了我们怎样都看不见歌单的消息。成功得到歌单信息之后,那么就可以开始我们的下载大业了,下面就开始说代码了,里面遇见的问题再一边分析。

使用scrapy进行爬虫,scrapy框架目录结构如下:

在使用命令行创建scrapy之后,首先是在setting.py中将机器人协议改为false,设置请求头,还有将piplines和middlewares的注释取消掉,这一些上一个项目的时候已经介绍过了,不清楚的可以翻一下上一篇,接下来是items.py中的内容

准备工作就做到这里了,接下来就开始分析各个代码了
首先是start_music163.py中的内容,这是用来启动这个爬虫的。

然后是爬虫中的内容,在爬虫开启webdriver,并且设置在爬虫关闭的时候,关闭chrome浏览器,接着讲请求发送出去。

class MusicSpiderSpider(scrapy.Spider):
name = 'music_spider'
allowed_domains = ['music.163.com']
start_urls = ['http://music.163.com/']
# first_url = 'https://music.163.com/#/playlist?id=107391599'
driver = webdriver.Chrome()
driver.set_page_load_timeout(30)

def __init__(self,gedan_url=None, *args, **kwargs):
self.first_url = gedan_url

#获得歌单链接之后,交给parse_catalog解析,但这里有开启中间件(middlewares.py)
#所以请求会先被传去middlewares.py
def start_requests(self):
yield scrapy.http.Request(url=self.first_url,callback=self.parse_catalog)

def closed(self, spider):
self.driver.close()

这里是比较重要的一个点。打开middlewares.py,middlewares(中间件)在url请求发送出去之后,会先经过中间件,这个时候我们可以把它捕获,通过selenium的page_source获得更加完整的主页代码,注意看代码中的注释,spider.driver.switch_to.frame(‘g_iframe’)用于解决上面提及的webdriver没有办法自动加载iframe的问题

import scrapy
class Music163DownloaderMiddleware(object):
#rrequest的请求被传到这里,接下来使用webdriver进行解析,然后再把响应返回给parse_catalog
def process_request(self, request, spider):
ids = request.url.split('=')[-1]
url = 'https://music.163.com/#/playlist?id=%s' % ids
spider.driver.get(url)
#注意:下面这一条代码很重要,因为webdriver无法显示出iframe中的东西
#所以需要使用switch_to.frame('g_iframe')来将隐藏的内容找出来
spider.driver.switch_to.frame('g_iframe')
source = spider.driver.page_source
return scrapy.http.HtmlResponse(url=url,body=source,encoding="utf-8",request=request)

之后我们回到爬虫中来对重新获得response进行数据清洗,通过xpath语法,成功将我们需要歌曲链接,歌曲名字和歌单名字全部传送给我们的piplines.py进行处理

#这里将歌单中每一首个的id和名字传到piplines.py进行处理
def parse_catalog(self, response):
# print(response.text)
songs_list = response.xpath('//table[@class="m-table "]/tbody/tr')
big_title = response.xpath('//h2[@class="f-ff2 f-brk"]/text()').get()
big_title = re.sub(r'[<>:"/\\|?*]', '', big_title)
for song in songs_list:
song_href = 'https://music.163.com/#'+song.xpath('.//a/@href').get()
song_title = song.xpath('.//b/@title').get()
song_ids = re.split('=',song_href)[-1]
song_title = re.sub(r'[<>:"/\\|?*]', '', song_title)
item = Music163Item(song_ids = song_ids,song_title = song_title,big_title=big_title)
yield item

下载也是一个很难的点,因为歌曲链接是jingguoAES加密的,没有学过这方面知识的朋友可能看不懂要如何解密,博主也没能好好理解怎么破解,但是github上的大佬是无所不能的,总会有大佬能成功破解的。

这里要好好感谢这位大佬,顺便贴上github大佬的网址:https://github.com/Jack-Cherish/python-spider/tree/master/Netease ,这位大佬有很多爬虫的项目 可以学习一下。那么我们就借助这位大佬的帮助,继续完成我们这个项目。
以下是那位大佬的破解过程,感兴趣的朋友可以好好研究一下

#下面这一部分是github上的大佬写的,用于破解网易云音乐链接的命名方式,看起来应该是涉及到AES了
#这部分没有专业知识应该是看不懂的,有兴趣的朋友可以研究一下
#github大佬的网址是:https://github.com/Jack-Cherish/python-spider/tree/master/Netease
#顺便一提 这位大佬有很多爬虫的项目 可以学习一下
class Encrypyed():
#解密算法
def __init__(self):
self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
self.nonce = '0CoJUm6Qyw8W8jud'
self.pub_key = '010001'
# 登录加密算法, 基于https://github.com/stkevintan/nw_musicbox脚本实现
def encrypted_request(self, text):
text = json.dumps(text)
sec_key = self.create_secret_key(16)
enc_text = self.aes_encrypt(self.aes_encrypt(text, self.nonce), sec_key.decode('utf-8'))
enc_sec_key = self.rsa_encrpt(sec_key, self.pub_key, self.modulus)
data = {'params': enc_text, 'encSecKey': enc_sec_key}
return data

def aes_encrypt(self, text, secKey):
pad = 16 - len(text) % 16
text = text + chr(pad) * pad
encryptor = AES.new(secKey.encode('utf-8'), AES.MODE_CBC, b'0102030405060708')
ciphertext = encryptor.encrypt(text.encode('utf-8'))
ciphertext = base64.b64encode(ciphertext).decode('utf-8')
return ciphertext

def rsa_encrpt(self, text, pubKey, modulus):
text = text[::-1]
rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16), int(modulus, 16))
return format(rs, 'x').zfill(256)

def create_secret_key(self, size):
return binascii.hexlify(os.urandom(size))[:16]

class Crawler():
def __init__(self, timeout=60, cookie_path='.'):
self.session = requests.Session()
self.session.headers.update(DEFAULT_REQUEST_HEADERS)
self.session.cookies = cookiejar.LWPCookieJar(cookie_path)
self.download_session = requests.Session()
self.timeout = timeout
self.ep = Encrypyed()

def post_request(self, url, params):
data = self.ep.encrypted_request(params)
resp = self.session.post(url, data=data, timeout=self.timeout)
result = resp.json()
# print(resp.url)
if result['code'] != 200:
click.echo('post_request error')
else:
return result

结果处理之后,我们得到了真正的下载链接,那么就可以开始下载了。这里利用requests.session进行下载,这种下载方式也可以用于下载视频,非常建议朋友们学习一下,这里附上文档http://docs.python-requests.org/zh_CN/latest/user/advanced.html

class Music163Pipeline(object):

def process_item(self, item, spider):
#下载路径
path = './'
gedang_name = os.path.join(path, item['big_title'])
if not os.path.exists(gedang_name):
os.mkdir(gedang_name)

song_id = item['song_ids']
song_title = item['song_title']

#将每一首歌的id进行解码,获得下载的url链接
csrf = ''
bit_rate = 320000
url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='
params = {'ids': [song_id], 'br': bit_rate, 'csrf_token': csrf}
crawl = Crawler()
result = crawl.post_request(url, params)
result_url = result['data'][0]['url']

#获得下载的url之后,便可以开始真正下载了
response = requests.session()
resp = response.get(result_url,stream=True)
with open(gedang_name+r'\\'+song_title+'.mp3','wb') as fp:
for chunk in resp.iter_content(chunk_size=1024):
if chunk:
fp.write(chunk)
return item

写到这里就大功告成了,接下来就是运行爬虫,等待结果了


大功告成,接下来我们就开始欣赏我们的音乐把,再次安利,这个歌单真的蛮不错的,喜欢纯音乐的朋友不要错过哦。我的分享就到这里了,要是大家能从里面收获到一点点,那对我来说便是极好了,我会把代码发到GitHub上,有兴趣的同学可以下载来研究一下!谢谢大家!
GitHub地址:https://github.com/weakmaple/music_163

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐