python实现某网站的音乐下载
写在前面:首先,理论上讲,如果歌曲可以在网页上播放,那么一定有网址(source src)保存着歌曲的源文件。那么利用火狐(或者谷歌)浏览器的F12功能,就可以快速提取出该source src,进而完成歌曲下载了。基于上述操作,我就想到了用python把如前所述封装起来,输入歌曲名称进行选择进而完成下载。
1. 前期准备
开发环境:win10 + py3.5(即windows + py3.x)
需要安装的库:requests 和 selenium(具体安装方法网上有很多,这里不再赘述。其中关于selenium的教程建议参考
虫师
的《selenium2 python自动化测试》)- ps. 这里的某网站指的是QQ音乐,当然酷狗音乐原理相同。网易云会涉及到更加复杂的跳转,由于没有学过前端,暂时无法处理。。。
2. 具体步骤
2.1 网址定位
打开QQ音乐,使用“搜索”按钮进行搜索后(以搜索-崇拜-为例),得到的结果为:
从图中复制可以得到歌曲搜索网址如下:
https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%B4%87%E6%8B%9C
注意:上述网址中的%E5%B4%87%E6%8B%9C
是“崇拜”的HTML可识别编码形式。
接下来我们使用requests.get(url)
就可以得到页面内容了,让我们看一下结果:
# coding:utf-8
# 测试返回结果
import requests
import urllib.request
headers = {
'Host': 'y.qq.com',
'User-Agent': '', # 这个大家用自己的就好
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate, br',
'Referer': 'http://y.qq.com/',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
}
MusicName = "崇拜" # 测试用例
urlMusicName = urllib.parse.quote(MusicName) # 转换成url可以读取的字符串
url = "https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=" + urlMusicName
response = requests.get(url, headers = headers)
print(response.text)
运行上述代码,打印结果如下所示:
由于结果太长,这里我们截取一部分观察。发现结果中出现很多乱码,原来是缺失对response
编码的结果。在上述代码中进行修改:
response = requests.get(url, headers = headers)
response.encoding = 'utf-8' # 对结果进行utf-8编码
此时再重新打印,就可以显示中文了!
但是我们会发现,在返回的结果中,并没有显示出歌曲列表,这是为什么呢?
对比一下原网页的F12结果和返回结果我们发现,在原网页中,歌曲列表是存在在这个div
里的:
<div class="js_search_tab_cont" id="song_box" style="display: block;">
但是在返回结果中,该div
标签的显示为:
<div class="js_search_tab_cont" id="song_box"></div>
内容为空!这也就解释了为什么我们无法得到歌曲列表了,因为根本没有加载出来。
为了进一步解释这个问题,我们需要做以下几件事:
打开火狐浏览器(因为我selenium用的火狐,所以这里都是使用火狐浏览器,当然使用谷歌也完全没有问题~~),在地址栏中输入
about:config
,进入浏览器设置页面。在页面中输入
javascript.enabled
,找到后 鼠标右键-切换。这样我们就将浏览器的
javascript
关掉了。关掉之后再重新加载原搜索网页,可以发现歌曲列表不见了。
通过上述操作,我们已经弄明白原因,那就是该歌曲列表是通过JavaScript
进行加载,而非静态网页,也因此,我们使用静态网页方法进行获取时,是得不到加载后的歌曲列表的。
为了解决上述问题,也查看了不少方法,觉得说的比较全面在这里:传送门-知乎
最后,我选择了一种最不智能的方法,就是—暗中观察。
继续F12,网络–JS–“clint_search”找到了js跳转的真正网址,具体如图:
这样就完成了请求网址的定位,万里长征走完第一步~
2.2 歌曲列表打印
可能有同学要问了,为什么我们要去定位上述网址呢,上述网址打开之后是什么样子呢?
乱乱的,像json
。为了方便阅读,这里直接使用火狐自带的阅读模式,就是上图中红色框中的button,点击,继续观察:
注意图中红色框出的部分,“media_mid”,“name”,“title”。后面两个很好解释,那么第一个是做什么用的呢?
在歌曲列表网页中随便点击一首歌曲,可以发现其跳转后的URL为:
https://y.qq.com/n/yqq/song/003JlYgD1SvCYe.html
后面这一长串就是media_mid,原来它是用来定位歌曲的。
具体如何从json中查找出全部有用信息,这就涉及到了python 正则匹配的问题,建议大家自行学习,这里匹配不难,注意细节即可!
将全部有用信息提取出来后,存入字典,并打印在屏幕上,这样就可以自己选择想下载的歌曲了。
其中,字符串的对齐需要注意一下。因为包含中文和标点符号,因此直接使用`”%-10s”等python自带的对齐方式是不行的,需要重新写对齐函数,我参考了这里中文字符对齐-传送门,并进行了部分修改,得到字符对齐函数如下:
# ================中文检测函数====================
# 参考网址:http://lib.csdn.net/article/python/66507?knId=160
def findChinese(source):
# text = source.decode('utf8') # python3 默认为unicode
r = re.findall('[\u4e00-\u9fa5]', source)
# 去除空格影响
condition = lambda t: t != ' '
results = list(filter(condition, r))
return results
# ===============填充字符函数======================
# 参考网址:http://lib.csdn.net/article/python/66507?knId=160
# 输入变量说明:
# un_align_str: 输入字符串
# lenh: 半角字符个数;自己设置;默认为0
# lenf: 全角字符个数;自己设置;默认为0
# addh: 半角字符空格
# addf: 全角字符空格
def myAlign(un_align_str, lenh = 0, lenf = 0, addh = ' ', addf = u' '):
assert isinstance(lenh, int) # 断言半角长度为整形变量
assert isinstance(lenf, int) # 断言全角长度为整形变量
slen = len(un_align_str)
# 中文在默认的utf-8编码下显示占用约2个字符
chn = findChinese(un_align_str)
numchn = len(chn)
numspn = slen - numchn
str = addh * (lenh - numspn) + addf * (lenf - 2 * numchn)
return str
OK,完成歌曲列表打印!
2.3 链接跳转与歌曲下载
我们发现,当我们点击歌曲的-播放-按钮时,实际上完成的是一次网页跳转(jump~jump~)
对于这种类似于人的操作,我们可以使用selenium来进行模拟。
具体过程也可以描述成如下步骤:
定位“播放”按钮
模拟鼠标左键单击操作
读取跳转后的网页,查找
source src
由于页面加载需要时间,这里我直接使用的是time.sleep()
函数来进行等待,另外针对多窗口之间的跳转,selenium也给出了API,那就是driver.window_handles
。
在这里,需要利用time.sleep()
等待句柄加载完成,否则会出现无法读取的情况,因此我用了如下判断语句进行处理
count = 0
allhandles = driver.window_handles
for handle in allhandles:
count += 1
if count == 2:
driver.switch_to_window(driver.window_handles[1])
else:
time.sleep(5)
driver.switch_to_window(driver.window_handles[1])
有了当前driver
,我们就可以读取driver.page_source
,正则匹配出source src
,然后利用requests.get(url).content
完成歌曲下载啦!
3. 相关源码
源码已经上传,大家可以自行下载:QQ音乐下载器源码。