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>

内容为空!这也就解释了为什么我们无法得到歌曲列表了,因为根本没有加载出来。

为了进一步解释这个问题,我们需要做以下几件事:

  1. 打开火狐浏览器(因为我selenium用的火狐,所以这里都是使用火狐浏览器,当然使用谷歌也完全没有问题~~),在地址栏中输入about:config,进入浏览器设置页面。

  2. 在页面中输入javascript.enabled,找到后 鼠标右键-切换。

  3. 这样我们就将浏览器的javascript关掉了。

  4. 关掉之后再重新加载原搜索网页,可以发现歌曲列表不见了。

通过上述操作,我们已经弄明白原因,那就是该歌曲列表是通过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来进行模拟。

具体过程也可以描述成如下步骤:

  1. 定位“播放”按钮

  2. 模拟鼠标左键单击操作

  3. 读取跳转后的网页,查找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音乐下载器源码

发布了21 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/fIsh1220Fish/article/details/78831690