1. 程式人生 > >python實現某網站的音樂下載

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音樂下載器原始碼