网易云音乐歌单

  |  

摘要: 爬虫获取异步加载网页的方法,ajax请求、Selenium、ChromeDriver

【对数据分析、人工智能、金融科技、风控服务感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:潮汐朝夕
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


动态网页入门 中,我们学习了如何在动态网页中获取数据,主要有逆向分析,以及使用 Selenium 的采集方案。本文我们以网易云音乐为例进行实践。

通过 sond_id

打开网页版网易云音乐,找到一首歌,例如下面这个链接的《New Boy》

https://music.163.com/#/song?id=1857630559

按 F12 的 Network 标签,进行逆向分析,主要看 xhr 和 media 这两种 Type,最终找到以下 media Type 的信息:

在资源的 Preview 信息中找到 URL,访问 URL 即可获得 m4a 文件,一般播放器都能打开。如果想要 mp3 文件,可以用 ffmpeg 过一遍即可:

1
ffmpeg -i 1.m4a 1.mp3

当然,也可以在以下链接中直接把 song_id 填入:

1
http://music.163.com/song/media/outer/url?id=这里填歌曲id.mp3

但是这个 URL 结构不是通过抓包获得的,遇到其他网站时就不一定能获得类似的方便链接了。

通过歌单 id

首先打开一份歌单,链接如下:

https://music.163.com/playlist?id=2785615092

上面链接中的 2785615092 是歌单 id。

基于前面的方便链接,可以写代码批量下载歌单 id 中的歌曲。

代码

代码的流程就是以下几步:

1
2
3
输入歌单 id,输出类似于 '/song?id=64006' 的各个歌曲的 info_str
输入歌曲的 info_str,输出歌曲的 id、名称
用歌曲 id 组装下载链接,保存文件名就是歌曲名称,执行下载

代码中用到的主要组件是 Scrapy、XPath、urllib、requests。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import os
import urllib

import requests
from scrapy.selector import Selector


class wangyiyun():
def __init__(self):
self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
,"Referer": "http://music.163.com/"
}
self.main_url = "http://music.163.com/"
self.dir_path = "./music"
self.session = requests.Session()
self.session.headers = self.headers

def get_songurls(self, playlist):
"""
进入所选歌单页面,得出歌单里每首歌各自的 ID 形式就是 “song?id=64006"
对于 playlist = 2785615092,结果如下:
['/song?id=64006', '/song?id=63959', '/song?id=25642714', '/song?id=63914', '/song?id=4878122', '/song?id=63650']
"""
url = self.main_url + "playlist?id={}".format(playlist)
response = self.session.get(url) # 直接用 session 进入网页
sel = Selector(text=response.text) # 用 scrapy 的 Selector
songurls = sel.xpath('//ul[@class="f-hide"]/li/a/@href').extract()
return songurls # 所有歌曲组成的list

def get_songinfo(self, songurl):
"""
根据 songid 进入每首歌信息的网址,得到歌曲的信息
对于 songurl="/song?id=64006",返回结果如下:
27556258, 群星-战斗(木桶进行曲)
"""
url = self.main_url + songurl
response = self.session.get(url)
sel = Selector(text=response.text)
song_id = url.split("=")[1]
song_name = sel.xpath("//em[@class='f-ff2']/text()").extract_first()
singer = "&".join(sel.xpath("//p[@class='des s-fc4']/span/a/text()").extract())
songname = singer + "-" + song_name
return str(song_id), songname

def download_song(self, songurl):
"""
根据歌曲 url,下载 mp3 文件
"""
song_id, songname = self.get_songinfo(songurl) # 根据歌曲 url 得出 ID、歌名
song_url = "http://music.163.com/song/media/outer/url?id={}.mp3".format(song_id) # 方便链接
save_path = self.dir_path + os.sep + songname + ".mp3" # 文件路径
urllib.request.urlretrieve(song_url, save_path) # 下载文件

def work(self, playlist):
songurls = self.get_songurls(playlist) # 输入歌单 ID,得到歌单所有歌曲的 url
for songurl in songurls:
self.download_song(songurl) # 下载歌曲


if __name__ == '__main__':
d = wangyiyun()
d.work(2785615092)

Share